Class: Rubber::Cloud::Vsphere

Inherits:
Fog
  • Object
show all
Defined in:
lib/rubber/cloud/vsphere.rb

Instance Attribute Summary

Attributes inherited from Base

#capistrano, #env

Instance Method Summary collapse

Methods inherited from Fog

#after_create_volume, #after_destroy_volume, #attach_static_ip, #before_create_volume, #before_destroy_volume, #compute_provider, #create_image, #create_static_ip, #describe_images, #describe_load_balancers, #describe_static_ips, #destroy_image, #destroy_spot_instance_request, #destroy_static_ip, #detach_static_ip, #reboot_instance, #start_instance, #stop_instance, #storage, #storage_provider, #table_store

Methods inherited from Base

#after_create_instance, #after_start_instance, #after_stop_instance, #before_create_instance, #before_refresh_instance, #before_start_instance, #before_stop_instance, #describe_security_groups, #inject_auto_security_groups, #isolate_group_name, #isolate_groups, #isolate_prefix, #setup_security_groups, #setup_vpc

Constructor Details

#initialize(env, capistrano) ⇒ Vsphere

Returns a new instance of Vsphere.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/rubber/cloud/vsphere.rb', line 6

def initialize(env, capistrano)
  compute_credentials = {
    :provider => 'vsphere',
    :vsphere_username => env.vcenter_username,
    :vsphere_password => env.vcenter_password,
    :vsphere_server => env.vcenter_host,
    :vsphere_expected_pubkey_hash => env.expected_pubkey_hash
  }

  if env.cloud_providers && env.cloud_providers.aws
    storage_credentials = {
        :provider => 'AWS',
        :aws_access_key_id => env.cloud_providers.aws.access_key,
        :aws_secret_access_key => env.cloud_providers.aws.secret_access_key,
        :path_style => true
    }

    storage_credentials[:region] = env.cloud_providers.aws.region

    env['storage_credentials'] = storage_credentials
  end

  env['compute_credentials'] = compute_credentials
  super(env, capistrano)
end

Instance Method Details

#active_stateObject



175
176
177
# File 'lib/rubber/cloud/vsphere.rb', line 175

def active_state
  'toolsOk'
end

#after_refresh_instance(instance) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/rubber/cloud/vsphere.rb', line 105

def after_refresh_instance(instance)
  super

  rubber_cfg = Rubber::Configuration.get_configuration(Rubber.env)
  host_env = rubber_cfg.environment.bind(nil, instance.name)

  dns_servers = [host_env.public_nic, env.public_nic, host_env.private_nic, env.private_nic].collect { |nic| nic.dns_servers if nic }.compact.first

  # VMware Tools currently (as of Aug. 25, 2014) has a bug with Ubuntu 14.04 whereby it fails to properly configure
  # DNS when static IP configurations are used in a customization spec.  This works around the problem by setting
  # up the resolvconf configuration directly.
  if dns_servers && ! dns_servers.empty?
    contents = dns_servers.map { |server| "nameserver #{server}" }.join("\n")
    contents << "\n"

    capistrano.put(contents, '/etc/resolvconf/resolv.conf.d/base', :mode => '0644', :hosts => instance.external_ip)
    capistrano.run('resolvconf -u', :hosts => instance.external_ip)
    end
end

#create_instance(instance_alias, image_name, image_type, security_groups, availability_zone, datacenter, fog_options = {}) ⇒ Object



32
33
34
35
36
37
38
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
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
# File 'lib/rubber/cloud/vsphere.rb', line 32

def create_instance(instance_alias, image_name, image_type, security_groups, availability_zone, datacenter, fog_options={})
  if env.domain.nil?
    raise "'domain' value must be configured"
  end

  rubber_cfg = Rubber::Configuration.get_configuration(Rubber.env)
  host_env = rubber_cfg.environment.bind(nil, instance_alias)

  if host_env.public_nic.nil? && host_env.private_nic.nil?
    raise "You must configure a private or a public NIC for this host in your rubber YAML"
  end

  if host_env.public_nic && env.public_network_name.nil?
    raise "You must configure the 'public_network_name' in the provider configuration"
  end

  if host_env.private_nic && env.private_network_name.nil?
    raise "You must configure the 'private_network_name' in the provider configuration"
  end

  nics = []

  if host_env.public_nic
    nic = nic_to_vsphere_config(host_env.public_nic, env.public_nic || {})
    validate_nic_vsphere_config(nic, :public)
    nics << nic
  end

  if host_env.private_nic
    nic = nic_to_vsphere_config(host_env.private_nic, env.private_nic || {})
    validate_nic_vsphere_config(nic, :private)
    nics << nic
  end

  vm_clone_options = {
    'datacenter' => datacenter,
    'template_path' => image_name,
    'name' => instance_alias,
    'power_on' => false
  }

  if host_env.memory
    vm_clone_options['memoryMB'] = host_env.memory
  end

  if host_env.cpus
    vm_clone_options['numCPUs'] = host_env.cpus
  end

  vm = compute_provider.vm_clone(vm_clone_options)

  server = compute_provider.servers.get(vm['new_vm']['id'])

  # Destroy all existing NICs.  We need the public and private IPs to line up with the NICs attached to the
  # correct virtual switches.  Rather than take the cross-product and try to work that out, it's easier to
  # just start fresh and guarantee everything works as intended.
  server.interfaces.each(&:destroy)

  server.interfaces.create(:network => env.public_network_name, :type => 'VirtualVmxnet3') if host_env.public_nic
  server.interfaces.create(:network => env.private_network_name, :type => 'VirtualVmxnet3') if host_env.private_nic

  vm_ref = compute_provider.send(:get_vm_ref, server.id)
  vm_ref.CustomizeVM_Task(:spec => customization_spec(instance_alias, nics))

  server.start

  vm['new_vm']['id']
end

#create_volume(instance, volume_spec) ⇒ Object



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/rubber/cloud/vsphere.rb', line 179

def create_volume(instance, volume_spec)
  server = compute_provider.servers.get(instance.instance_id)
  datastore = volume_spec['datastore']
  thin_disk = volume_spec.has_key?('thin') ? volume_spec['thin'] : true

  # This craziness here is so we can map the device name to an appropriate SCSI channel index, which is zero-based.
  # E.g., /dev/sdc would correspond to a unit_number of 2.  We do this by chopping off the SCSI device letter and
  # then doing some ASCII value math to convert to the appropriate decimal value.
  unit_number = volume_spec['device'][-1].ord - 97

  config = { :size_gb => volume_spec['size'], :unit_number => unit_number }

  if datastore
    config[:datastore] = datastore
  end

  unless thin_disk
    eager_zero = volume_spec.has_key?('eager_zero') ? volume_spec['eager_zero'] : false

    config[:thin] = false
    config[:eager_zero] = eager_zero
  end

  fog_options = Rubber::Util.symbolize_keys(volume_spec['fog_options'] || {})
  volume = server.volumes.create(config.merge(fog_options))

  volume.id
end

#describe_instances(instance_id = nil) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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
# File 'lib/rubber/cloud/vsphere.rb', line 125

def describe_instances(instance_id=nil)
  instances = []
  opts = {}

  if instance_id
    response = [compute_provider.servers.get(instance_id)]
  else
    response = compute_provider.servers.all(opts)
  end

  response.each do |item|
    rubber_cfg = Rubber::Configuration.get_configuration(Rubber.env)
    host_env = rubber_cfg.environment.bind(nil, item.name)

    instance = {}
    instance[:id] = item.id

    # If the VM is up, but the VMware Tools installation is out-of-date, just treat it as if the tools installation
    # is okay.  Otherwise, the rubber:refresh process won't detect that the VM has booted properly, even though
    # it's running just fine.  In all other cases, use the state reported by the vSphere API call.
    instance[:state] = item.tools_state == 'toolsOld' ? active_state : item.tools_state

    # We can't trust the describe operation when the instance is newly created because the VM customization
    # step likely hasn't completed yet.  This means we'll get back the IP address for the VM template, rather
    # than the one we just configured.
    if host_env.public_nic
      instance[:external_ip] = host_env.public_nic.ip_address

      if host_env.private_nic.nil?
        instance[:internal_ip] = host_env.public_nic.ip_address
      end
    end

    if host_env.private_nic
      instance[:internal_ip] = host_env.private_nic.ip_address

      if host_env.public_nic.nil?
        instance[:external_ip] = host_env.private_nic.ip_address
      end
    end

    instance[:region_id] = item.datacenter
    instance[:provider] = 'vsphere'
    instance[:platform] = Rubber::Platforms::LINUX
    instances << instance
  end

  return instances
end

#describe_volumes(volume_id = nil) ⇒ Object



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
# File 'lib/rubber/cloud/vsphere.rb', line 219

def describe_volumes(volume_id=nil)
  volumes = []
  opts = {}
  opts[:'volume-id'] = volume_id if volume_id

  if volume_id
    response = [compute_provider.servers.collect { |s| s.volumes.all }.flatten.find { |v| v.id == volume_id }]
  else
    response = compute_provider.servers.collect { |s| s.volumes.all }.flatten
  end

  response.each do |item|
    volume = {}
    volume[:id] = item.id
    volume[:status] = item.unit_number == 0 ? 'root' : 'extra'

    if item.server_id
      volume[:attachment_instance_id] = item.server_id
      volume[:attachment_status] = Thread.current[:detach_volume] == item.id ? 'detached' : 'attached'
    end

    volumes << volume
  end

  volumes
end

#destroy_instance(instance_id) ⇒ Object



101
102
103
# File 'lib/rubber/cloud/vsphere.rb', line 101

def destroy_instance(instance_id)
  compute_provider.servers.get(instance_id).destroy(:force => true)
end

#destroy_volume(volume_id) ⇒ Object



208
209
210
211
212
213
214
215
216
217
# File 'lib/rubber/cloud/vsphere.rb', line 208

def destroy_volume(volume_id)
  # TODO (nirvdrum 10/28/13): Fog currently lacks the ability to fetch a volume by ID, so we need to fetch all volumes for all servers to find the volume we want.  This is terribly inefficient and fog should be updated.
  volume = compute_provider.servers.collect { |s| s.volumes.all }.flatten.find { |v| v.id == volume_id }

  if volume.unit_number == 0
    raise "Cannot destroy volume because it is the VM root device.  Destroy the VM if you really want to free this volume."
  end

  volume.destroy
end

#should_destroy_volume_when_instance_destroyed?Boolean

Returns:

  • (Boolean)


246
247
248
# File 'lib/rubber/cloud/vsphere.rb', line 246

def should_destroy_volume_when_instance_destroyed?
  true
end

#should_disable_password_based_ssh_login?Boolean

Returns:

  • (Boolean)


250
251
252
# File 'lib/rubber/cloud/vsphere.rb', line 250

def should_disable_password_based_ssh_login?
  true
end