-
Notifications
You must be signed in to change notification settings - Fork 982
/
vmware.rb
214 lines (175 loc) · 5.36 KB
/
vmware.rb
1
2
3
4
5
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
31
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
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
212
213
require 'fog_extensions/vsphere/mini_servers'
require 'foreman/exception'
require 'rbvmomi'
module Foreman::Model
class Vmware < ComputeResource
validates_presence_of :user, :password, :server, :datacenter
before_create :update_public_key
def self.model_name
ComputeResource.model_name
end
def capabilities
[:build]
end
def vms(opts = {})
if opts[:eager_loading] == true
super()
else
#VMWare server loading is very slow
#not using FOG models directly to save the time
#and minimize the amount of time required (as we don't require all attributes by default when listing)
FogExtensions::Vsphere::MiniServers.new(client, datacenter)
end
end
def provided_attributes
super.merge({ :mac => :mac })
end
def max_cpu_count (cluster = nil)
return 8 unless cluster
cluster.num_cpu_cores
end
def max_memory
16*1024*1024*1024
end
def datacenters
client.datacenters.all
end
def clusters
dc.clusters
end
def folders
dc.vm_folders.sort_by{|f| f.path}
end
def networks
dc.networks.all(:accessible => true)
end
def nictypes
{
"VirtualE1000" => "E1000",
"VirtualVmxnet3" => "VMXNET 3"
}
end
def datastores
dc.datastores.all(:accessible => true)
end
def test_connection options = {}
super
if errors[:server].empty? and errors[:user].empty? and errors[:password].empty?
update_public_key options
datacenters
end
rescue => e
errors[:base] << e.message
end
def new_vm attr={ }
test_connection
return unless errors.empty?
opts = vm_instance_defaults.merge(attr.to_hash).symbolize_keys
# convert rails nested_attributes into a plain hash
[:interfaces, :volumes].each do |collection|
nested_attrs = opts.delete("#{collection}_attributes".to_sym)
opts[collection] = nested_attributes_for(collection, nested_attrs) if nested_attrs
end
opts.reject! { |k, v| v.nil? }
client.servers.new opts
end
def create_vm args = { }
dc_networks = networks
args["interfaces_attributes"].each do |key, interface|
# Convert interface type to RbVmomi class
unless nictypes.has_key? interface["type"]
raise "Unknown NIC type: #{interface["type"]}"
end
interface["type"] = ("RbVmomi::VIM::" + interface["type"]).constantize
# Convert network id into name
net = dc_networks.find { |n| n.id == interface["network"] }
raise "Unknown Network ID: #{interface["network"]}" if net.nil?
interface["network"] = net.name
end
vm = new_vm(args)
vm.save
rescue Fog::Errors::Error => e
logger.debug e.backtrace
errors.add(:base, e.to_s)
false
end
def server
url
end
def server= value
self.url = value
end
def datacenter
uuid
end
def datacenter= value
self.uuid = value
end
def console uuid
vm = find_vm_by_uuid(uuid)
raise "VM is not running!" unless vm.ready?
#TOOD port, password
#NOTE this requires the following port to be open on your ESXi FW
values = { :port => unused_vnc_port(vm.hypervisor), :password => random_password, :enabled => true }
vm.config_vnc(values)
WsProxy.start(:host => vm.hypervisor, :host_port => values[:port], :password => values[:password]).merge(:type => 'vnc')
end
def new_interface attr = { }
client.interfaces.new attr
end
def new_volume attr = { }
client.volumes.new attr.merge(:size_gb => 10)
end
def pubkey_hash
attrs[:pubkey_hash]
end
def pubkey_hash= key
attrs[:pubkey_hash] = key
end
def associated_host(vm)
Host.my_hosts.where(:mac => vm.mac).first
end
private
def dc
client.datacenters.get(datacenter)
end
def update_public_key options ={}
return unless pubkey_hash.blank? || options[:force]
client
rescue Foreman::FingerprintException => e
self.pubkey_hash = e.fingerprint
end
def client
@client ||= ::Fog::Compute.new(
:provider => "vsphere",
:vsphere_username => user,
:vsphere_password => password,
:vsphere_server => server,
:vsphere_expected_pubkey_hash => pubkey_hash
)
rescue => e
if e.message =~ /The remote system presented a public key with hash (\w+) but we're expecting a hash of/
raise Foreman::FingerprintException.new(
N_("The remote system presented a public key with hash %s but we're expecting a different hash. If you are sure the remote system is authentic, go to the compute resource edit page, press the 'Test Connection' or 'Load Datacenters' button and submit"), $1)
else
raise e
end
end
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
raise "no unused port found"
end
def vm_instance_defaults
super.merge(
:memory_mb => 768,
:interfaces => [new_interface],
:volumes => [new_volume],
:datacenter => datacenter
)
end
end
end