Statistics
| Branch: | Tag: | Revision:

one / src / onedb / vcenter_one54_pre.rb @ 593600f7

History | View | Annotate | Download (128 KB)

1
#!/usr/bin/env ruby
2

    
3
# -------------------------------------------------------------------------- #
4
# Copyright 2002-2017, OpenNebula Project, OpenNebula Systems                #
5
#                                                                            #
6
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
7
# not use this file except in compliance with the License. You may obtain    #
8
# a copy of the License at                                                   #
9
#                                                                            #
10
# http://www.apache.org/licenses/LICENSE-2.0                                 #
11
#                                                                            #
12
# Unless required by applicable law or agreed to in writing, software        #
13
# distributed under the License is distributed on an "AS IS" BASIS,          #
14
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
15
# See the License for the specific language governing permissions and        #
16
# limitations under the License.                                             #
17
#--------------------------------------------------------------------------- #
18

    
19
#!/usr/bin/env ruby
20

    
21
ONE_LOCATION=ENV["ONE_LOCATION"]
22

    
23
if !ONE_LOCATION
24
    RUBY_LIB_LOCATION="/usr/lib/one/ruby"
25
    REMOTES_LOCATION="/var/lib/one/remotes/"
26
else
27
    RUBY_LIB_LOCATION=ONE_LOCATION+"/lib/ruby"
28
    REMOTES_LOCATION=ONE_LOCATION+"/var/remotes/"
29
end
30

    
31
$: << RUBY_LIB_LOCATION
32
$: << RUBY_LIB_LOCATION+"/cli"
33
$: << REMOTES_LOCATION+"vmm/vcenter/"
34

    
35
require 'fileutils'
36

    
37
require 'command_parser'
38
require 'one_helper/onehost_helper'
39
require 'one_helper/onecluster_helper'
40
require 'vcenter_driver'
41
require 'opennebula'
42

    
43
TEMP_DIR="/var/tmp/vcenter_one54"
44
FileUtils.mkdir_p TEMP_DIR
45

    
46
def banner(msg, header=false, extended=nil)
47
    STDOUT.puts
48
    STDOUT.puts if !header
49
    STDOUT.puts "="*80
50
    STDOUT.puts msg
51
    STDOUT.puts "-"*80 if extended
52
    STDOUT.puts extended if extended
53
    STDOUT.puts "="*80
54
end
55

    
56
def logo_banner(msg, header=false)
57

    
58
    STDOUT.puts
59
    STDOUT.puts if !header
60
    STDOUT.puts "="*80
61
    STDOUT.puts " / _ \\ _ __   ___ _ __ | \\ | | ___| |__  _   _| | __ _"
62
    STDOUT.puts "| | | | '_ \\ / _ \\ '_ \\|  \\| |/ _ \\ '_ \\| | | | |/ _` |"
63
    STDOUT.puts "| |_| | |_) |  __/ | | | |\\  |  __/ |_) | |_| | | (_| |"
64
    STDOUT.puts " \\___/| .__/ \\___|_| |_|_| \\_|\\___|_.__/ \\__,_|_|\\__,_|"
65
    STDOUT.puts "      |_|"
66
    STDOUT.puts "-"*80
67
    STDOUT.puts msg
68
    STDOUT.puts "="*80
69
end
70

    
71
################################################################################
72
# Monkey patch XMLElement with retrieve_xmlelements
73
################################################################################
74

    
75
class OpenNebula::XMLElement
76
    def retrieve_xmlelements(xpath_str)
77
        collection = []
78
        if OpenNebula::NOKOGIRI
79
            @xml.xpath(xpath_str).each { |pelem|
80
                collection << OpenNebula::XMLElement.new(pelem)
81
            }
82
        else
83
            @xml.elements.each(xpath_str) { |pelem|
84
                collection << OpenNebula::XMLElement.new(pelem)
85
            }
86
        end
87
        collection
88
    end
89
end
90

    
91
def get_image_size(ds, img_str)
92
    ds_name = ds.name
93

    
94
    img_path = File.dirname img_str
95
    img_name = File.basename img_str
96

    
97
    # Create Search Spec
98
    spec = RbVmomi::VIM::HostDatastoreBrowserSearchSpec.new
99

    
100
    vmdisk_query = RbVmomi::VIM::VmDiskFileQuery.new
101
    vmdisk_query.details = RbVmomi::VIM::VmDiskFileQueryFlags(:diskType        => true,
102
                                                              :capacityKb      => true,
103
                                                              :hardwareVersion => true,
104
                                                              :controllerType  => true)
105

    
106
    spec.query   = [vmdisk_query, RbVmomi::VIM::IsoImageFileQuery.new]
107
    spec.details = RbVmomi::VIM::FileQueryFlags(:fileOwner    => true,
108
                                                :fileSize     => true,
109
                                                :fileType     => true,
110
                                                :modification => true)
111

    
112
    spec.matchPattern = img_name.nil? ? [] : [img_name]
113

    
114
    datastore_path = "[#{ds_name}]"
115
    datastore_path << " #{img_path}" if !img_path.nil?
116

    
117
    search_params = {'datastorePath' => datastore_path, 'searchSpec'    => spec}
118

    
119
    # Perform search task and return results
120
    begin
121
        search_task = ds.browser.SearchDatastoreSubFolders_Task(search_params)
122

    
123
        search_task.wait_for_completion
124

    
125
        size = 0
126

    
127
        # Try to get vmdk capacity as seen by VM
128
        size = search_task.info.result[0].file[0].capacityKb / 1024 rescue nil
129

    
130
        # Try to get file size
131
        size = search_task.info.result[0].file[0].fileSize / 1024 / 1024 rescue nil if !size
132

    
133
        raise "Could not get file size or capacity" if size.nil?
134

    
135
        size
136
    rescue
137
        raise "Could not find file #{img_path}."
138
    end
139
end
140

    
141
def create_cdata_element(item, xml_doc, element_name, element_value)
142
    item.add_child(xml_doc.create_element(element_name)).add_child(Nokogiri::XML::CDATA.new(xml_doc,element_value))
143
end
144

    
145
def create_disk(xml_doc, image_name, image_source, image_prefix, image_id,
146
                disk_index, cluster_id, ds, ds_ref, ds_name, vi_client)
147

    
148
    disk_size = get_image_size(RbVmomi::VIM::Datastore.new(vi_client.vim, ds_ref), image_source)
149
    device_letters = ('a'..'z').to_a
150

    
151
    # Add new disk attributes
152
    xml_template   = xml_doc.root.at_xpath("TEMPLATE")
153
    disk = xml_template.add_child(xml_doc.create_element("DISK"))
154
    create_cdata_element(disk, xml_doc, "CLONE", "YES")
155
    create_cdata_element(disk, xml_doc, "CLONE_TARGET", "SYSTEM")
156
    create_cdata_element(disk, xml_doc, "CLUSTER_ID", "#{cluster_id}")
157
    create_cdata_element(disk, xml_doc, "DATASTORE", "#{ds_name}")
158
    create_cdata_element(disk, xml_doc, "DATASTORE_ID", "#{ds["ID"]}")
159
    create_cdata_element(disk, xml_doc, "DEV_PREFIX", "#{image_prefix}")
160
    create_cdata_element(disk, xml_doc, "DISK_ID", "#{disk_index}")
161
    create_cdata_element(disk, xml_doc, "DISK_SNAPSHOT_TOTAL_SIZE", "0")
162
    create_cdata_element(disk, xml_doc, "DISK_TYPE", "FILE")
163
    create_cdata_element(disk, xml_doc, "IMAGE", "#{image_name}")
164
    create_cdata_element(disk, xml_doc, "IMAGE_ID", "#{image_id}")
165
    create_cdata_element(disk, xml_doc, "IMAGE_STATE", "2")
166
    create_cdata_element(disk, xml_doc, "LN_TARGET", "NONE")
167
    create_cdata_element(disk, xml_doc, "OPENNEBULA_MANAGED", "NO")
168
    create_cdata_element(disk, xml_doc, "ORIGINAL_SIZE", "#{disk_size}")
169
    create_cdata_element(disk, xml_doc, "READONLY", "NO")
170
    create_cdata_element(disk, xml_doc, "SAVE", "NO")
171
    create_cdata_element(disk, xml_doc, "SIZE", "#{disk_size}")
172
    create_cdata_element(disk, xml_doc, "SOURCE", "#{image_source}")
173
    create_cdata_element(disk, xml_doc, "TARGET", "#{image_prefix}#{device_letters[disk_index]}")
174
    create_cdata_element(disk, xml_doc, "TM_MAD", "vcenter")
175
    create_cdata_element(disk, xml_doc, "TYPE", "FILE")
176
    create_cdata_element(disk, xml_doc, "VCENTER_ADAPTER_TYPE","#{ds["TEMPLATE/VCENTER_DS_REF"]}")
177
    create_cdata_element(disk, xml_doc, "VCENTER_DISK_TYPE", "#{ds["TEMPLATE/VCENTER_DS_REF"]}")
178
    create_cdata_element(disk, xml_doc, "VCENTER_DS_REF", "#{ds["TEMPLATE/VCENTER_DS_REF"]}")
179
end
180

    
181
def create_nic(xml_doc, network, mac_address, cluster_id, nic_index)
182
    xml_template   = xml_doc.root.at_xpath("TEMPLATE")
183
    nic = xml_template.add_child(xml_doc.create_element("NIC"))
184
    create_cdata_element(nic, xml_doc, "BRIDGE", "#{network["BRIDGE"]}")
185
    create_cdata_element(nic, xml_doc, "CLUSTER_ID", "#{cluster_id}")
186
    create_cdata_element(nic, xml_doc, "MAC", "#{mac_address}")
187
    create_cdata_element(nic, xml_doc, "NETWORK", "#{network["NAME"]}")
188
    create_cdata_element(nic, xml_doc, "NETWORK_ID", "#{network["ID"]}")
189
    create_cdata_element(nic, xml_doc, "NIC_ID", "#{nic_index}")
190
    create_cdata_element(nic, xml_doc, "OPENNEBULA_MANAGED", "NO")
191
    create_cdata_element(nic, xml_doc, "SECURITY_GROUPS", "0")
192
    create_cdata_element(nic, xml_doc, "VCENTER_CCR_REF", "#{network["TEMPLATE/VCENTER_CCR_REF"]}")
193
    create_cdata_element(nic, xml_doc, "VCENTER_INSTANCE_ID", "#{network["TEMPLATE/VCENTER_INSTANCE_ID"]}")
194
    create_cdata_element(nic, xml_doc, "VCENTER_NET_REF", "#{network["TEMPLATE/VCENTER_NET_REF"]}")
195
    create_cdata_element(nic, xml_doc, "VCENTER_PORTGROUP_TYPE", "#{network["TEMPLATE/VCENTER_PORTGROUP_TYPE"]}")
196
    create_cdata_element(nic, xml_doc, "VN_MAD", "dummy")
197
end
198

    
199
def create_image_ds(ds_name, ds_ref, vcenter_name, vcenter_uuid, ccr_name, dc_name, dc_ref, one_client, vcenter_user, vcenter_pass, vcenter_host, cluster_id=nil)
200
    image_ds_name = "#{ds_name}"
201
    template  = ""
202
    template  << "NAME=\"#{image_ds_name}\"\n"
203
    template  << "TM_MAD=vcenter\n"
204
    template  << "VCENTER_INSTANCE_ID=\"#{vcenter_uuid}\"\n"
205
    template  << "VCENTER_DS_REF=\"#{ds_ref}\"\n"
206
    template  << "VCENTER_DC_REF=\"#{dc_ref}\"\n"
207
    template  << "VCENTER_DS_NAME=\"#{ds_name}\"\n"
208
    template  << "VCENTER_DC_NAME=\"#{dc_name}\"\n"
209
    template  << "VCENTER_CLUSTER=\"#{ccr_name}\"\n"
210
    template  << "TYPE=IMAGE_DS\n"
211
    template  << "DS_MAD=vcenter\n"
212

    
213
    one_ds = OpenNebula::Datastore.new(OpenNebula::Datastore.build_xml, one_client)
214
    rc = one_ds.allocate(template)
215
    raise rc.message if OpenNebula.is_error?(rc)
216

    
217
    rc = one_ds.info
218
    raise rc.message if OpenNebula.is_error?(rc)
219

    
220
    # Wait till the DS is monitored
221
    loop do
222
        sleep(2)
223
        one_ds.info
224
        break if one_ds["STATE"].to_i == 0 && one_ds["TOTAL_MB"].to_i > 0
225
    end
226

    
227
    STDOUT.puts "--- New IMAGE datastore #{image_ds_name} created with ID: #{one_ds["ID"]}!\n"
228

    
229
    return one_ds
230
end
231

    
232
def create_system_ds(ds_name, ds_ref, vcenter_name, vcenter_uuid, dc_name, dc_ref, one_client, vcenter_host, vcenter_user, vcenter_pass, cluster_id=nil)
233
    system_ds_name = "#{ds_name} (SYS)"
234
    template  = ""
235
    template  << "NAME=\"#{system_ds_name}\"\n"
236
    template  << "TM_MAD=vcenter\n"
237
    template  << "VCENTER_INSTANCE_ID=\"#{vcenter_uuid}\"\n"
238
    template  << "VCENTER_DC_REF=\"#{dc_ref}\"\n"
239
    template  << "VCENTER_DC_NAME=\"#{dc_name}\"\n"
240
    template  << "VCENTER_DS_REF=\"#{ds_ref}\"\n"
241
    template  << "VCENTER_DS_NAME=\"#{ds_name}\"\n"
242
    template  << "VCENTER_USER=\"#{vcenter_user}\"\n" if vcenter_user
243
    template  << "VCENTER_PASSWORD=\"#{vcenter_pass}\"\n" if vcenter_pass
244
    template  << "VCENTER_HOST=\"#{vcenter_host}\"\n" if vcenter_host
245
    template  << "TYPE=SYSTEM_DS\n"
246

    
247
    one_ds = OpenNebula::Datastore.new(OpenNebula::Datastore.build_xml, one_client)
248
    if cluster_id
249
        rc = one_ds.allocate(template, cluster_id.to_i)
250
    else
251
        rc = one_ds.allocate(template)
252
    end
253
    raise rc.message if OpenNebula.is_error?(rc)
254

    
255
    one_ds.info
256
    rc = one_ds.info
257
    raise rc.message if OpenNebula.is_error?(rc)
258

    
259
    STDOUT.puts "Datastore \e[96m#{ds_name}\e[39m is now also a SYSTEM datastore with ID: #{one_ds["ID"]}\n"
260
end
261

    
262
def get_dc(item)
263
    while !item.instance_of? RbVmomi::VIM::Datacenter
264
        item = item.parent
265
        raise "Could not find the parent Datacenter" if !item
266
    end
267
    return item
268
end
269

    
270
def find_image(ipool, ds_name, image_path)
271
    element = ipool.select do |e|
272
        e["SOURCE"] == image_path &&
273
        e["DATASTORE"] == ds_name
274

    
275
    end.first rescue nil
276

    
277
    return element
278
end
279

    
280
def find_datastore_by_name(dspool, name)
281
    element = dspool.select do |e|
282
        e["NAME"] == name
283
    end.first rescue nil
284

    
285
    return element
286
end
287

    
288
def find_cluster_by_name(cpool, name)
289
    element = cpool.select do |e|
290
        e["NAME"] == name
291
    end.first rescue nil
292

    
293
    return element
294
end
295

    
296
def find_datastore(dspool, ds_ref, dc_ref, vcenter_uuid, type)
297
    element = dspool.select do |e|
298
        e["TEMPLATE/TYPE"]                == type &&
299
        e["TEMPLATE/VCENTER_DS_REF"]      == ds_ref &&
300
        e["TEMPLATE/VCENTER_DC_REF"]      == dc_ref &&
301
        e["TEMPLATE/VCENTER_INSTANCE_ID"] == vcenter_uuid
302
    end.first rescue nil
303

    
304
    return element
305
end
306

    
307
def find_network(vnpool, net_ref, ccr_ref, template_ref, vcenter_uuid)
308
    element = vnpool.select do |e|
309
        e["TEMPLATE/VCENTER_NET_REF"]      == net_ref &&
310
        e["TEMPLATE/VCENTER_TEMPLATE_REF"] == template_ref &&
311
        e["TEMPLATE/VCENTER_CCR_REF"]      == ccr_ref &&
312
        e["TEMPLATE/VCENTER_INSTANCE_ID"]  == vcenter_uuid &&
313
        e["TEMPLATE/OPENNEBULA_MANAGED"] == "NO"
314
    end.first rescue nil
315

    
316
    return element
317
end
318

    
319
def find_template(tpool, template_id, template_ref, ccr_ref, vcenter_uuid)
320
    element = tpool.select do |e|
321
        e["ID"] == template_id &&
322
        e["TEMPLATE/VCENTER_TEMPLATE_REF"] == template_ref &&
323
        e["TEMPLATE/VCENTER_CCR_REF"]      == ccr_ref &&
324
        e["TEMPLATE/VCENTER_INSTANCE_ID"]  == vcenter_uuid
325
    end.first rescue nil
326

    
327
    return element
328
end
329

    
330
def vm_unmanaged_discover(devices, xml_doc, template_xml,
331
                          existing_disks, existing_nics, ccr_name, ccr_ref,
332
                          vcenter_name, vcenter_uuid, vcenter_user, vcenter_pass, vcenter_host,
333
                          dc_name, dc_ref, ipool, vnpool, dspool, hpool,
334
                          one_client, vi_client, vm_wild, vm_id, vm_name, vc_templates,
335
                          vm_ref, vc_vmachines, template_ref, one_clusters, vcenter_ids)
336
    unmanaged = {}
337
    unmanaged[:images] = []
338
    unmanaged[:networks] = []
339

    
340
    ide_controlled  = []
341
    sata_controlled = []
342
    scsi_controlled = []
343

    
344
    disk_index           = 0
345
    unmanaged_disk_index = 0
346
    managed_disk_index   = 0
347
    nic_index            = 0
348
    managed_nic_index    = 0
349

    
350
    extraconfig   = []
351

    
352
    # Get cluster's host ID
353
    hosts = hpool.retrieve_xmlelements("HOST[NAME=\"#{ccr_name}\"]")
354
    if hosts.empty?
355
        raise "Cannot find cluster's host ID associated with this template."
356
    end
357
    host_id = hosts.first["ID"]
358

    
359
    if !one_clusters.key?(host_id)
360
        raise "Could not find the OpenNebula cluster ID that is going to be associated to images and networks found in the template."
361
    end
362

    
363
    cluster_id = one_clusters[host_id]
364

    
365
    devices.each do |device|
366
        rc = vnpool.info_all
367
        raise "\n    ERROR! Could not update vnpool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
368

    
369
        rc = ipool.info_all
370
        raise "\n    ERROR! Could not update ipool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
371

    
372
        rc = dspool.info
373
        raise "\n    ERROR! Could not update dspool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
374

    
375
        if defined?(RbVmomi::VIM::VirtualIDEController) &&
376
           device.is_a?(RbVmomi::VIM::VirtualIDEController)
377
            ide_controlled += device.device
378
            next
379
        end
380

    
381
        if defined?(RbVmomi::VIM::VirtualSATAController) &&
382
           device.is_a?(RbVmomi::VIM::VirtualSATAController)
383
            sata_controlled += device.device
384
            next
385
        end
386

    
387
        if defined?(RbVmomi::VIM::VirtualSCSIController) &&
388
           device.is_a?(RbVmomi::VIM::VirtualSCSIController)
389
            scsi_controlled += device.device
390
            next
391
        end
392

    
393
        #cluster_id = xml_doc.root.xpath("HISTORY_RECORDS/HISTORY[last()]/CID").text
394

    
395
        # If CDROM
396
        if !(device.class.ancestors.index(RbVmomi::VIM::VirtualCdrom)).nil?
397
            ds_name       = device.backing.datastore.name
398
            ds_ref        = device.backing.datastore._ref
399
            image_path    = device.backing.fileName.sub(/^\[(.*?)\] /, "")
400

    
401
            image = find_image(ipool, ds_name, image_path)
402

    
403
            if image
404
                # It's a persistent disk if it already exists
405
                xml_template     = xml_doc.root.at_xpath("TEMPLATE")
406
                existing_disk    = existing_disks[managed_disk_index]
407

    
408
                # Replace DISK_ID
409
                existind_disk_id = existing_disk.at_xpath("DISK_ID")
410
                existind_disk_id.content = disk_index
411

    
412
                disk = xml_template.add_child(existing_disks[managed_disk_index])
413

    
414
                # Add VCENTER_DS_REF
415
                disk.add_child(xml_doc.create_element("VCENTER_DS_REF")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{ds_ref}"))
416

    
417
                STDOUT.puts "--- Added VCENTER_DS_REF=#{ds_ref} to CDROM (IMAGE_ID=#{image["ID"]})"
418

    
419
                # Update indexes
420
                managed_disk_index = managed_disk_index + 1
421
                disk_index = disk_index + 1
422
            end
423
        end
424

    
425
        # If Virtual Disk
426
        if !(device.class.ancestors.index(RbVmomi::VIM::VirtualDisk)).nil?
427
            ds_name       = device.backing.datastore.name
428
            ds_ref        = device.backing.datastore._ref
429
            image_type    = "OS"
430
            image_path    = device.backing.fileName.sub(/^\[(.*?)\] /, "")
431
            image_prefix  = "hd" if ide_controlled.include?(device.key)
432
            image_prefix  = "sd" if scsi_controlled.include?(device.key)
433
            image_prefix  = "sd" if sata_controlled.include?(device.key)
434
            file_name     = File.basename(image_path).gsub(/\.vmdk$/,"")
435
            image_name    = "#{file_name} - #{ds_name}"
436

    
437
            #Check if the image already exists
438
            one_image = find_image(ipool, ds_name, image_path)
439

    
440
            if !one_image
441
                #Check if the IMAGE DS is there
442
                ds = find_datastore(dspool, ds_ref, dc_ref, vcenter_uuid, "IMAGE_DS")
443

    
444
                #Create IMAGE and SYSTEM DS if datastore is not found
445
                if !ds
446
                    ds = create_image_ds(ds_name, ds_ref, vcenter_name, vcenter_uuid, ccr_name, dc_name, dc_ref, one_client, vcenter_user, vcenter_pass, vcenter_host, cluster_id)
447

    
448
                    create_system_ds(ds_name, ds_ref, vcenter_name, vcenter_uuid, dc_name, dc_ref, one_client, vcenter_user, vcenter_pass, vcenter_host, cluster_id)
449
                end
450

    
451
                ds_id = ds["ID"].to_i
452

    
453
                if vm_wild || !template_xml
454
                    #Create images for unmanaged disks
455

    
456
                    template = ""
457
                    template << "NAME=\"#{image_name}\"\n"
458
                    template << "PATH=\"vcenter://#{image_path}\"\n"
459
                    template << "TYPE=\"#{image_type}\"\n"
460
                    template << "PERSISTENT=\"NO\"\n"
461
                    template << "VCENTER_IMPORTED=\"YES\"\n"
462
                    template << "DEV_PREFIX=\"#{image_prefix}\"\n"
463

    
464
                    one_image = OpenNebula::Image.new(OpenNebula::Image.build_xml, one_client)
465
                    rc = one_image.allocate(template, ds_id)
466
                    raise "\n    ERROR! Could not create image for wild vm. Reason #{rc.message}" if OpenNebula.is_error?(rc)
467

    
468
                    loop do
469
                        rc = one_image.info
470
                        raise "\n    ERROR! Could not get image info for wild vm disk. Reason #{rc.message}" if OpenNebula.is_error?(rc)
471
                        break if one_image["SOURCE"] && !one_image["SOURCE"].empty? #Get out of loop if image is not locked
472
                        sleep(1)
473
                    end
474

    
475
                    if one_image["STATE"] == 5
476
                        raise "\n    ERROR! The image created for wild vm is in ERROR state"
477
                    end
478

    
479
                    vcenter_ids[:image] << one_image["ID"]
480

    
481
                    STDOUT.puts "--- Image #{one_image["NAME"]} with ID #{one_image["ID"]} has been created"
482
                else
483
                    template_disk = template_xml.xpath("VMTEMPLATE/TEMPLATE/DISK")[unmanaged_disk_index] rescue nil
484
                    raise "Cannot find unmanaged disk inside template" if !template_disk
485

    
486
                    image_id = template_disk.xpath("IMAGE_ID").text
487
                    raise "Cannot find image id for unmanaged disk" if image_id.empty?
488

    
489
                    one_image = OpenNebula::Image.new_with_id(image_id, one_client)
490
                    rc = one_image.info
491
                    raise "\n    ERROR! Could not get image info for unmanaged disk. image_id '#{image_id}'. Reason #{rc.message}" if OpenNebula.is_error?(rc)
492

    
493
                    ds_id = one_image['DATASTORE_ID']
494
                    ds = OpenNebula::Datastore.new_with_id(ds_id, one_client)
495
                    rc = ds.info
496
                    raise "\n    ERROR! Could not get ds info. Reason #{rc.message}" if OpenNebula.is_error?(rc)
497

    
498
                    ds_ref = ds["TEMPLATE/VCENTER_DS_REF"]
499
                    ds_name = ds["NAME"]
500

    
501
                    STDOUT.puts "--- Image #{one_image["NAME"]} with ID #{one_image["ID"]} already exists"
502
                end
503

    
504
                # Create unmanaged disk element for vm template
505
                # Get disk size (capacity)
506
                image_name   = one_image["NAME"]
507
                image_source = one_image["SOURCE"]
508
                image_id     = one_image["ID"]
509

    
510
                # Add new disk attributes
511
                create_disk(xml_doc, image_name, image_source, image_prefix, image_id,
512
                            disk_index, cluster_id, ds, ds_ref, ds_name, vi_client)
513

    
514
                STDOUT.puts "--- Added unmanaged disk to xml template (IMAGE_ID=#{one_image["ID"]})"
515

    
516
                reference = {}
517
                reference[:key]   = "opennebula.disk.#{unmanaged_disk_index}"
518
                reference[:value] = "#{device.key}"
519
                extraconfig << reference
520

    
521
                unmanaged_disk_index = unmanaged_disk_index + 1
522
                disk_index = disk_index + 1
523
            else
524

    
525
                if one_image["TEMPLATE/VCENTER_IMPORTED"] == "YES"
526

    
527
                    #Check if the IMAGE DS is there
528
                    ds = find_datastore(dspool, ds_ref, dc_ref, vcenter_uuid, "IMAGE_DS")
529

    
530
                    #Create IMAGE and SYSTEM DS if datastore is not found
531
                    if !ds
532
                        ds = create_image_ds(ds_name, ds_ref, vcenter_name, vcenter_uuid, ccr_name, dc_name, dc_ref, one_client, vcenter_user, vcenter_pass, vcenter_host, cluster_id)
533

    
534
                        create_system_ds(ds_name, ds_ref, vcenter_name, vcenter_uuid, dc_name, dc_ref, one_client, vcenter_user, vcenter_pass, vcenter_host, cluster_id)
535
                    end
536

    
537
                    ds_id = ds["ID"].to_i
538

    
539
                    #Create unmanaged disk element for vm template
540
                    image_id     = one_image["ID"]
541
                    image_name   = one_image["NAME"]
542
                    image_source = one_image["SOURCE"]
543

    
544
                    create_disk(xml_doc, image_name, image_source, image_prefix, image_id,
545
                                disk_index, cluster_id, ds, ds_ref, ds_name, vi_client)
546

    
547
                    STDOUT.puts "--- Added unmanaged disk in wild vm (IMAGE_ID=#{one_image["ID"]})"
548

    
549
                    reference = {}
550
                    reference[:key]   = "opennebula.disk.#{unmanaged_disk_index}"
551
                    reference[:value] = "#{device.key}"
552
                    extraconfig << reference
553

    
554
                    # Update indexes
555
                    unmanaged_disk_index = unmanaged_disk_index + 1
556
                    disk_index = disk_index + 1
557

    
558
                else
559
                    # It's a persistent disk if it already exists
560
                    xml_template     = xml_doc.root.at_xpath("TEMPLATE")
561
                    existing_disk    = existing_disks[managed_disk_index]
562

    
563
                    # Replace DISK_ID
564
                    existing_disk_image_id = existing_disk.xpath("IMAGE_ID").text
565
                    existing_disk_id = existing_disk.at_xpath("DISK_ID")
566
                    existing_disk_id.content = disk_index
567

    
568
                    disk = xml_template.add_child(existing_disks[managed_disk_index])
569

    
570
                    # Add VCENTER_DISK_TYPE and VCENTER_ADAPTER_TYPE if found
571
                    if !existing_disk.xpath("DISK_TYPE").text.empty?
572
                        disk.add_child(xml_doc.create_element("VCENTER_DISK_TYPE")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{existing_disk.xpath("DISK_TYPE").text}"))
573
                        STDOUT.puts "--- Added VCENTER_DISK_TYPE=#{existing_disk.xpath("DISK_TYPE").text} to existing disk (IMAGE_ID=#{existing_disk_image_id})"
574
                    end
575

    
576
                    if !existing_disk.xpath("ADAPTER_TYPE").text.empty?
577
                        disk.add_child(xml_doc.create_element("VCENTER_ADAPTER_TYPE")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{existing_disk.xpath("ADAPTER_TYPE").text}"))
578
                        STDOUT.puts "--- Added VCENTER_ADAPTER_TYPE=#{existing_disk.xpath("ADAPTER_TYPE").text} to existing disk (IMAGE_ID=#{existing_disk_image_id})"
579
                    end
580

    
581
                    # Add VCENTER_DS_REF
582
                    disk.add_child(xml_doc.create_element("VCENTER_DS_REF")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{ds_ref}"))
583
                    STDOUT.puts "--- Added VCENTER_DS_REF=#{ds_ref} to existing disk (IMAGE_ID=#{existing_disk_image_id})"
584

    
585
                    # Update indexes
586
                    managed_disk_index = managed_disk_index + 1
587
                    disk_index = disk_index + 1
588
                end
589
            end
590
        end
591

    
592
        # If VirtualEthernetCard
593
        if !device.class.ancestors.index(RbVmomi::VIM::VirtualEthernetCard).nil?
594
            network_bridge = device.backing.network.name
595
            network_ref    = device.backing.network._ref
596
            network_name   = "#{network_bridge} [#{vm_name}]"
597
            network_type   = device.backing.network.instance_of?(RbVmomi::VIM::DistributedVirtualPortgroup) ? "Distributed Port Group" : "Port Group"
598

    
599
            # Create network if doesn't exist
600
            network = find_network(vnpool, network_ref, ccr_ref, template_ref, vcenter_uuid)
601

    
602
            if !network
603
                one_net = ""
604
                one_net << "NAME=\"#{network_name}\"\n"
605
                one_net << "BRIDGE=\"#{network_bridge}\"\n"
606
                one_net << "VN_MAD=\"dummy\"\n"
607
                one_net << "VCENTER_PORTGROUP_TYPE=\"#{network_type}\"\n"
608
                one_net << "VCENTER_NET_REF=\"#{network_ref}\"\n"
609
                one_net << "VCENTER_CCR_REF=\"#{ccr_ref}\"\n"
610
                one_net << "VCENTER_INSTANCE_ID=\"#{vcenter_uuid}\"\n"
611
                one_net << "VCENTER_TEMPLATE_REF=\"#{template_ref}\"\n"
612
                one_net << "OPENNEBULA_MANAGED=\"NO\"\n"
613
                one_net << "AR=[\n"
614
                one_net << "TYPE=\"ETHER\",\n"
615
                one_net << "SIZE=\"255\"\n"
616
                one_net << "]\n"
617

    
618
                one_vn = OpenNebula::VirtualNetwork.new(OpenNebula::VirtualNetwork.build_xml, one_client)
619
                rc = one_vn.allocate(one_net, cluster_id.to_i)
620
                raise "\n    ERROR! Could not create vnet for vm #{vm_name}. Reason #{rc.message}" if OpenNebula.is_error?(rc)
621

    
622
                rc = one_vn.info
623
                raise "\n    ERROR! Could not get network info for vnet #{network_name}. Reason #{rc.message}" if OpenNebula.is_error?(rc)
624
                network = one_vn
625
                STDOUT.puts "--- Network #{one_vn["NAME"]} with ID #{one_vn["ID"]} has been created"
626
            else
627
                STDOUT.puts "--- Network #{network["NAME"]} with ID #{network["ID"]} already exists"
628
            end
629

    
630
            existing_macs = []
631
            existing_nics.xpath("MAC").each do |mac|
632
                existing_macs << mac.text
633
            end
634

    
635
            mac_address = device.macAddress
636
            if !existing_macs.include?(mac_address)
637
                # Unmanaged nic
638
                create_nic(xml_doc, network, mac_address, cluster_id, nic_index)
639

    
640
                #Update indexes
641
                nic_index = nic_index + 1
642
            else
643
                # Managed nic
644
                managed_nic_index = existing_macs.index(mac_address)
645
                xml_template      = xml_doc.root.at_xpath("TEMPLATE")
646
                existing_nic      = existing_nics[managed_nic_index]
647

    
648
                # Replace NIC_ID
649
                existind_nic_id = existing_nic.at_xpath("NIC_ID")
650
                existind_nic_id.content = nic_index
651

    
652
                # Add existing NIC to XML template
653
                nic = xml_template.add_child(existing_nics[managed_nic_index])
654
                create_cdata_element(nic, xml_doc, "VCENTER_NET_REF", "#{network["TEMPLATE/VCENTER_NET_REF"]}")
655
                create_cdata_element(nic, xml_doc, "VCENTER_CCR_REF", "#{network["TEMPLATE/VCENTER_CCR_REF"]}")
656
                create_cdata_element(nic, xml_doc, "VCENTER_INSTANCE_ID", "#{network["TEMPLATE/VCENTER_INSTANCE_ID"]}")
657
                create_cdata_element(nic, xml_doc, "VCENTER_PORTGROUP_TYPE", "#{network["TEMPLATE/VCENTER_PORTGROUP_TYPE"]}")
658

    
659
                #Update indexes
660
                nic_index = nic_index + 1
661
            end
662
        end
663
    end
664

    
665
    # Reference some nodes
666
    xml_template = xml_doc.root.at_xpath("TEMPLATE")
667
    xml_user_template = xml_doc.root.at_xpath("USER_TEMPLATE")
668
    xml_monitoring = xml_doc.root.at_xpath("MONITORING")
669
    xml_history = xml_doc.root.at_xpath("HISTORY_RECORDS/HISTORY")
670

    
671
    # Update context disk ID
672
    if !vm_wild
673
        xml_template.xpath("CONTEXT/DISK_ID").remove
674
        xml_context  = xml_template.at_xpath("CONTEXT")
675
        xml_context.add_child(xml_doc.create_element("DISK_ID")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{disk_index}"))
676
    end
677

    
678
    # Remove PUBLIC_CLOUD as it is no longer used with vcenter
679
    xml_user_template.xpath("PUBLIC_CLOUD").remove
680

    
681
    # Remove KEEP_DISKS_ON_DONE from USER_TEMPLATE
682
    xml_user_template.xpath("KEEP_DISKS_ON_DONE").remove
683

    
684
    # Remove SCHED_DS_REQUIREMENTS
685
    xml_user_template.xpath("SCHED_DS_REQUIREMENTS").remove
686

    
687
    # Remove VCENTER_DATASTORE and USER_INPUTS/VCENTER_DATASTORE... no longer used
688
    # If datastore is a system_ds we may have to add SCHED_DS_REQUIREMENTS
689
    # Also identify the datastore id to update last history records with the new DS_ID
690

    
691
    vcenter_datastore = xml_user_template.xpath("VCENTER_DATASTORE").text
692
    ds_id = nil
693
    if !vcenter_datastore.empty?
694
        # If VCENTER_DATASTORE is a SYSTEM_DS it is a StoragePod then
695
        # SCHED_DS_REQUIREMENTS must contain ID="DS_ID"
696
        ds      = find_datastore_by_name(dspool, "#{vcenter_datastore}")
697
        ds_id   = ds["ID"]
698
        ds_type = ds["TEMPLATE/TYPE"]
699
        if ds_type == "SYSTEM_DS"
700
            sched_ds_req = xml_user_template.xpath("SCHED_DS_REQUIREMENTS")
701
            if !sched_ds_req.empty?
702
                xml_user_template.xpath("SCHED_DS_REQUIREMENTS").remove
703
                requirements = "ID=#{ds_id} & (#{sched_ds_req})"
704
                xml_user_template.add_child(xml_doc.create_element("SCHED_DS_REQUIREMENTS")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"\"#{requirements}\""))
705
            else
706
                # Add a SCHED_DS_REQUIREMENTS to template
707
                xml_user_template.add_child(xml_doc.create_element("SCHED_DS_REQUIREMENTS")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"\"ID=#{ds_id}\""))
708
            end
709
        end
710

    
711
        # New ds should be a system datastore
712
        ds = find_datastore_by_name(dspool, "#{vcenter_datastore} (SYS)")
713
        ds_id = ds["ID"]
714
    else
715
        if vm_wild
716
            if !vc_vmachines.key? vm_ref
717
                raise "Could not find vcenter vm using ref #{vm_ref} in order to assign a datastore"
718
            else
719
                ds_ref = vc_vmachines[vm_ref]["datastore"].first._ref rescue nil
720
                raise "Could not get ds ref in order to assign a datastore in history records" if !ds_ref
721

    
722
                ds = find_datastore(dspool, ds_ref, dc_ref, vcenter_uuid, "SYSTEM_DS")
723
                ds_id = ds["ID"]
724
            end
725
        else
726
            if !vc_templates.key? template_ref
727
                raise "Could not find vcenter template using ref #{template_ref} in order to assign a datastore"
728
            else
729
                ds_ref = vc_templates[template_ref]["datastore"].first._ref rescue nil
730
                raise "Could not get ds ref in order to assign a datastore in history records" if !ds_ref
731

    
732
                ds = find_datastore(dspool, ds_ref, dc_ref, vcenter_uuid, "SYSTEM_DS")
733
                ds_id = ds["ID"]
734
            end
735
        end
736
    end
737

    
738
    # Remove some attributes
739
    xml_user_template.xpath("VCENTER_DATASTORE").remove
740
    xml_user_template.xpath("USER_INPUTS/VCENTER_DATASTORE").remove
741
    xml_user_template.xpath("SCHED_REQUIREMENTS").remove
742

    
743
    # Replace USER_TEMPLATE/RESOURCE_POOL with VCENTER_RESOURCE_POOL
744
    resource_pool = xml_user_template.xpath("RESOURCE_POOL").text
745
    if !resource_pool.empty?
746
        xml_user_template.xpath("RESOURCE_POOL").remove
747
        vcenter_rp = xml_user_template.xpath("VCENTER_RESOURCE_POOL").text
748
        if !vcenter_rp.empty?
749
            xml_user_template.xpath("VCENTER_RESOURCE_POOL").remove
750
        end
751
        xml_user_template.add_child(xml_doc.create_element("VCENTER_RESOURCE_POOL")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{resource_pool}"))
752
    end
753

    
754
    # Replace USER_INPUTS/RESOURCE_POOL... no longer used
755
    user_resource_pool = xml_user_template.xpath("USER_INPUTS/RESOURCE_POOL").text
756
    if !user_resource_pool.empty?
757
        xml_user_template.xpath("USER_INPUTS/RESOURCE_POOL").remove
758
        vcenter_rp = xml_user_template.xpath("USER_INPUTS/VCENTER_RESOURCE_POOL").text
759
        if !vcenter_rp.empty?
760
            xml_user_template.xpath("USER_INPUTS/VCENTER_RESOURCE_POOL").remove
761
        end
762
        user_inputs = xml_user_template.at_xpath("USER_INPUTS")
763
        user_inputs.add_child(xml_doc.create_element("VCENTER_RESOURCE_POOL")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{user_resource_pool}"))
764
    end
765

    
766
    # Replace CUSTOMIZATION_SPEC with VCENTER_CUSTOMIZATION_SPEC
767
    customization_spec = xml_user_template.xpath("CUSTOMIZATION_SPEC").text
768
    if !customization_spec.empty?
769
        xml_user_template.xpath("CUSTOMIZATION_SPEC").remove
770
        xml_user_template.add_child(xml_doc.create_element("VCENTER_CUSTOMIZATION_SPEC")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{customization_spec}"))
771
    end
772

    
773
    # Add VCENTER_CCR_REF and VCENTER_INSTANCE_ID
774
    vcenter_ccr_ref = xml_user_template.xpath("VCENTER_CCR_REF").text
775
    if !vcenter_ccr_ref.empty?
776
        xml_user_template.xpath("VCENTER_CCR_REF").remove
777
    end
778
    xml_user_template.add_child(xml_doc.create_element("VCENTER_CCR_REF")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{ccr_ref}"))
779

    
780
    vcenter_instance_id = xml_user_template.xpath("VCENTER_INSTANCE_ID").text
781
    if !vcenter_instance_id.empty?
782
        xml_user_template.xpath("VCENTER_INSTANCE_ID").remove
783
    end
784
    xml_user_template.add_child(xml_doc.create_element("VCENTER_INSTANCE_ID")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{vcenter_uuid}"))
785

    
786
    # Add VCENTER_TEMPLATE_REF if VM is not wild
787
    if !vm_wild
788
        vcenter_template_ref = xml_user_template.xpath("VCENTER_TEMPLATE_REF").text
789
        if !vcenter_template_ref.empty?
790
            xml_user_template.xpath("VCENTER_TEMPLATE_REF").remove
791
        end
792
        xml_user_template.add_child(xml_doc.create_element("VCENTER_TEMPLATE_REF")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{template_ref}"))
793
    end
794

    
795
    # Monitoring info attributes
796
    xml_monitoring.xpath("LAST_MON").remove
797

    
798
    esx_host = xml_monitoring.xpath("ESX_HOST").text
799
    if !esx_host.empty?
800
        xml_monitoring.xpath("ESX_HOST").remove
801
        xml_monitoring.add_child(xml_doc.create_element("VCENTER_ESX_HOST")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{esx_host}"))
802
    end
803

    
804
    guest_state = xml_monitoring.xpath("GUEST_STATE").text
805
    if !guest_state.empty?
806
        xml_monitoring.xpath("GUEST_STATE").remove
807
        xml_monitoring.add_child(xml_doc.create_element("VCENTER_GUEST_STATE")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{guest_state}"))
808
    end
809

    
810
    resource_pool = xml_monitoring.xpath("RESOURCE_POOL").text
811
    if !guest_state.empty?
812
        xml_monitoring.xpath("RESOURCE_POOL").remove
813
        xml_monitoring.add_child(xml_doc.create_element("VCENTER_RP_NAME")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{resource_pool}"))
814
    end
815

    
816
    vmware_running_status = xml_monitoring.xpath("VMWARETOOLS_RUNNING_STATUS").text
817
    if !vmware_running_status.empty?
818
        xml_monitoring.xpath("VMWARETOOLS_RUNNING_STATUS").remove
819
        xml_monitoring.add_child(xml_doc.create_element("VCENTER_VMWARETOOLS_RUNNING_STATUS")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{vmware_running_status}"))
820
    end
821

    
822
    vmware_tools_version = xml_monitoring.xpath("VMWARETOOLS_VERSION").text
823
    if !vmware_tools_version.empty?
824
        xml_monitoring.xpath("VMWARETOOLS_VERSION").remove
825
        xml_monitoring.add_child(xml_doc.create_element("VCENTER_VMWARETOOLS_VERSION")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{vmware_tools_version}"))
826
    end
827

    
828
    vmware_tools_version_status = xml_monitoring.xpath("VMWARETOOLS_VERSION_STATUS").text
829
    if !vmware_tools_version_status.empty?
830
        xml_monitoring.xpath("VMWARETOOLS_VERSION_STATUS").remove
831
        xml_monitoring.add_child(xml_doc.create_element("VCENTER_VMWARETOOLS_VERSION_STATUS")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{vmware_tools_version_status}"))
832
    end
833

    
834
    # History record info attributes
835
    vmware_tools_version = xml_history.xpath("VMWARETOOLS_VERSION").text
836
    history_ds_id = xml_history.at_xpath("DS_ID")
837
    history_ds_id.content = ds_id
838
    xml_history.xpath("TM_MAD").remove
839
    xml_history.add_child(xml_doc.create_element("TM_MAD")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"vcenter"))
840

    
841
    # Write template to file
842
    File.open("#{TEMP_DIR}/one_migrate_vm_#{vm_id}","w"){|f| f.puts(xml_doc.root.to_s.gsub(/>\s*/, ">").gsub(/\s*</, "<"))}
843
    STDOUT.puts
844
    STDOUT.puts "--- New XML file #{TEMP_DIR}/one_migrate_vm_#{vm_id} for vm \e[96m#{vm_name}\e[39m \e[92mwas created and attributes were removed\e[39m\n"
845

    
846
    return extraconfig
847
end
848

    
849
def template_unmanaged_discover(devices, ccr_name, ccr_ref,
850
                                vcenter_name, vcenter_uuid,
851
                                vcenter_user, vcenter_pass, vcenter_host,
852
                                dc_name, dc_ref, ipool, vnpool, dspool, hpool,
853
                                one_client,
854
                                template_ref, template_name, template_id,
855
                                one_clusters, vcenter_ids)
856
    unmanaged = {}
857
    unmanaged[:images] = []
858
    unmanaged[:networks] = []
859

    
860
    ide_controlled  = []
861
    sata_controlled = []
862
    scsi_controlled = []
863

    
864
    # Get cluster's host ID
865
    hosts = hpool.retrieve_xmlelements("HOST[NAME=\"#{ccr_name}\"]")
866
    if hosts.empty?
867
        raise "Cannot find cluster's host ID associated with this template."
868
    end
869
    host_id = hosts.first["ID"]
870

    
871
    if !one_clusters.key?(host_id)
872
        raise "Could not find the OpenNebula cluster ID that is going to be associated to images and networks found in the template."
873
    end
874

    
875
    cluster_id = one_clusters[host_id]
876

    
877
    # Loop through devices
878
    devices.each do |device|
879
        rc = vnpool.info_all
880
        raise "\n    ERROR! Could not update vnpool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
881

    
882
        rc = ipool.info_all
883
        raise "\n    ERROR! Could not update ipool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
884

    
885
        rc = dspool.info
886
        raise "\n    ERROR! Could not update dspool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
887

    
888
        if defined?(RbVmomi::VIM::VirtualIDEController) &&
889
           device.is_a?(RbVmomi::VIM::VirtualIDEController)
890
            ide_controlled += device.device
891
            next
892
        end
893

    
894
        if defined?(RbVmomi::VIM::VirtualSATAController) &&
895
           device.is_a?(RbVmomi::VIM::VirtualSATAController)
896
            sata_controlled += device.device
897
            next
898
        end
899

    
900
        if defined?(RbVmomi::VIM::VirtualSCSIController) &&
901
            device.is_a?(RbVmomi::VIM::VirtualSCSIController)
902
            scsi_controlled += device.device
903
            next
904
        end
905

    
906
        # If Virtual Disk
907
        if !(device.class.ancestors.index(RbVmomi::VIM::VirtualDisk)).nil?
908
            ds_name       = device.backing.datastore.name
909
            ds_ref        = device.backing.datastore._ref
910
            image_path    = device.backing.fileName.sub(/^\[(.*?)\] /, "")
911
            image_type    = "OS"
912
            image_prefix  = "hd" if ide_controlled.include?(device.key)
913
            image_prefix  = "sd" if scsi_controlled.include?(device.key)
914
            image_prefix  = "sd" if sata_controlled.include?(device.key)
915
            file_name     = File.basename(image_path).gsub(/\.vmdk$/,"")
916
            image_name    = "#{file_name} - #{ds_name} [Template #{template_id}]"
917

    
918
            #Check if the image has already been imported
919
            image = find_image(ipool, ds_name, image_path)
920

    
921
            #Check if the IMAGE DS is there
922
            ds = find_datastore(dspool, ds_ref, dc_ref, vcenter_uuid, "IMAGE_DS")
923

    
924
            #Create IMAGE and SYSTEM DS if datastore is not found
925
            if ds.nil?
926
                ds = create_image_ds(ds_name, ds_ref, vcenter_name, vcenter_uuid, ccr_name, dc_name, dc_ref, one_client, vcenter_user, vcenter_pass, vcenter_host, cluster_id)
927

    
928
                create_system_ds(ds_name, ds_ref, vcenter_name, vcenter_uuid, dc_name, dc_ref, one_client, vcenter_user, vcenter_pass, vcenter_host, cluster_id)
929
            end
930

    
931
            if !image
932
                #Create image
933
                one_image = ""
934
                one_image << "NAME=\"#{image_name}\"\n"
935
                one_image << "PATH=\"vcenter://#{image_path}\"\n"
936
                one_image << "TYPE=\"#{image_type}\"\n"
937
                one_image << "PERSISTENT=\"NO\"\n"
938
                one_image << "VCENTER_IMPORTED=\"YES\"\n"
939
                one_image << "DEV_PREFIX=\"#{image_prefix}\"\n"
940

    
941
                one_i = OpenNebula::Image.new(OpenNebula::Image.build_xml, one_client)
942
                rc = one_i.allocate(one_image, ds["ID"].to_i)
943
                raise "\n    ERROR! Could not create image for template #{template_name}. Reason #{rc.message}" if OpenNebula.is_error?(rc)
944

    
945
                rc = one_i.info
946
                raise "\n    ERROR! Could not get image info for template #{template_name}. Reason #{rc.message}" if OpenNebula.is_error?(rc)
947
                STDOUT.puts "--- Image #{one_i["NAME"]} with ID #{one_i["ID"]} has been created"
948
                unmanaged[:images] << one_i["ID"]
949

    
950
                vcenter_ids[:image] << one_i["ID"]
951
            else
952
                unmanaged[:images] << image["ID"]
953
                STDOUT.puts "--- Image #{image["NAME"]} with ID #{image["ID"]} already exists"
954
            end
955
        end
956

    
957
        # If VirtualEthernetCard
958
        if !device.class.ancestors.index(RbVmomi::VIM::VirtualEthernetCard).nil?
959
            network_bridge = device.backing.network.name
960
            network_ref    = device.backing.network._ref
961
            network_name   = "#{network_bridge} [#{template_name} - Template #{template_id}]"
962
            network_type   = device.backing.network.instance_of?(RbVmomi::VIM::DistributedVirtualPortgroup) ? "Distributed Port Group" : "Port Group"
963

    
964
            network = find_network(vnpool, network_ref, ccr_ref, template_ref, vcenter_uuid)
965

    
966
            if !network
967
                one_net = ""
968
                one_net << "NAME=\"#{network_name}\"\n"
969
                one_net << "BRIDGE=\"#{network_bridge}\"\n"
970
                one_net << "VN_MAD=\"dummy\"\n"
971
                one_net << "VCENTER_PORTGROUP_TYPE=\"#{network_type}\"\n"
972
                one_net << "VCENTER_NET_REF=\"#{network_ref}\"\n"
973
                one_net << "VCENTER_CCR_REF=\"#{ccr_ref}\"\n"
974
                one_net << "VCENTER_INSTANCE_ID=\"#{vcenter_uuid}\"\n"
975
                one_net << "VCENTER_TEMPLATE_REF=\"#{template_ref}\"\n"
976
                one_net << "OPENNEBULA_MANAGED=\"NO\"\n"
977
                one_net << "AR=[\n"
978
                one_net << "TYPE=\"ETHER\",\n"
979
                one_net << "SIZE=\"255\"\n"
980
                one_net << "]\n"
981

    
982
                one_vn = OpenNebula::VirtualNetwork.new(OpenNebula::VirtualNetwork.build_xml, one_client)
983
                rc = one_vn.allocate(one_net, cluster_id.to_i)
984
                raise "\n    ERROR! Could not create vnet for template #{template_name}. Reason #{rc.message}" if OpenNebula.is_error?(rc)
985

    
986
                rc = one_vn.info
987
                raise "\n    ERROR! Could not get network info for template #{template_name}. Reason #{rc.message}" if OpenNebula.is_error?(rc)
988
                STDOUT.puts "--- Network #{one_vn["NAME"]} with ID #{one_vn["ID"]} has been created"
989
                unmanaged[:networks] << one_vn["ID"]
990
            else
991
                unmanaged[:networks] << network["ID"]
992
                STDOUT.puts "--- Network #{network["NAME"]} with ID #{network["ID"]} already exists"
993
            end
994
        end
995
    end
996

    
997
    return unmanaged
998
end
999

    
1000
def retrieve_vcenter_clusters(vi_client)
1001

    
1002
    view = vi_client.vim.serviceContent.viewManager.CreateContainerView({
1003
            container: vi_client.vim.rootFolder,
1004
            type:      ['ClusterComputeResource'],
1005
            recursive: true
1006
    })
1007

    
1008
    pc = vi_client.vim.serviceContent.propertyCollector
1009

    
1010
    filterSpec = RbVmomi::VIM.PropertyFilterSpec(
1011
        :objectSet => [
1012
            :obj => view,
1013
            :skip => true,
1014
            :selectSet => [
1015
            RbVmomi::VIM.TraversalSpec(
1016
                :name => 'traverseEntities',
1017
                :type => 'ContainerView',
1018
                :path => 'view',
1019
                :skip => false
1020
            )
1021
            ]
1022
        ],
1023
        :propSet => [
1024
            { :type => 'ClusterComputeResource', :pathSet => ['name','host'] }
1025
        ]
1026
    )
1027

    
1028
    result = pc.RetrieveProperties(:specSet => [filterSpec])
1029

    
1030
    clusters = {}
1031
    result.each do |r|
1032
        clusters[r.obj._ref] = r.to_hash if r.obj.is_a?(RbVmomi::VIM::ClusterComputeResource)
1033
    end
1034

    
1035
    view.DestroyView # Destroy the view
1036

    
1037
    return clusters
1038
end
1039

    
1040
def retrieve_vcenter_datastores(vi_client)
1041

    
1042
    view = vi_client.vim.serviceContent.viewManager.CreateContainerView({
1043
            container: vi_client.vim.rootFolder,
1044
            type:      ['Datastore','StoragePod'],
1045
            recursive: true
1046
    })
1047

    
1048
    pc = vi_client.vim.serviceContent.propertyCollector
1049

    
1050
    filterSpec = RbVmomi::VIM.PropertyFilterSpec(
1051
        :objectSet => [
1052
            :obj => view,
1053
            :skip => true,
1054
            :selectSet => [
1055
            RbVmomi::VIM.TraversalSpec(
1056
                :name => 'traverseEntities',
1057
                :type => 'ContainerView',
1058
                :path => 'view',
1059
                :skip => false
1060
            )
1061
            ]
1062
        ],
1063
        :propSet => [
1064
            { :type => 'Datastore', :pathSet => ['name'] },
1065
            { :type => 'StoragePod', :pathSet => ['name'] }
1066
        ]
1067
    )
1068

    
1069
    result = pc.RetrieveProperties(:specSet => [filterSpec])
1070

    
1071
    datastores = {}
1072
    result.each do |r|
1073
        datastores[r.obj._ref] = r.to_hash if r.obj.is_a?(RbVmomi::VIM::Datastore) || r.obj.is_a?(RbVmomi::VIM::StoragePod)
1074
        datastores[r.obj._ref][:ds_type] = r.obj.is_a?(RbVmomi::VIM::Datastore) ? "Datastore" : "StoragePod"
1075
    end
1076

    
1077
    view.DestroyView # Destroy the view
1078

    
1079
    return datastores
1080
end
1081

    
1082
def retrieve_vcenter_networks(vi_client)
1083

    
1084
    view = vi_client.vim.serviceContent.viewManager.CreateContainerView({
1085
        container: vi_client.vim.rootFolder,
1086
        type:      ['Network','DistributedVirtualPortgroup'],
1087
        recursive: true
1088
    })
1089

    
1090
    pc = vi_client.vim.serviceContent.propertyCollector
1091

    
1092
    filterSpec = RbVmomi::VIM.PropertyFilterSpec(
1093
        :objectSet => [
1094
            :obj => view,
1095
            :skip => true,
1096
            :selectSet => [
1097
            RbVmomi::VIM.TraversalSpec(
1098
                :name => 'traverseEntities',
1099
                :type => 'ContainerView',
1100
                :path => 'view',
1101
                :skip => false
1102
            )
1103
            ]
1104
        ],
1105
        :propSet => [
1106
            { :type => 'Network', :pathSet => ['name','host'] },
1107
            { :type => 'DistributedVirtualPortgroup', :pathSet => ['name','host'] }
1108
        ]
1109
    )
1110

    
1111
    result = pc.RetrieveProperties(:specSet => [filterSpec])
1112

    
1113
    networks = {}
1114
    result.each do |r|
1115
        networks[r.obj._ref] = r.to_hash if r.obj.is_a?(RbVmomi::VIM::DistributedVirtualPortgroup) || r.obj.is_a?(RbVmomi::VIM::Network)
1116
        networks[r.obj._ref][:network_type] = r.obj.is_a?(RbVmomi::VIM::DistributedVirtualPortgroup) ? "Distributed Port Group" : "Port Group"
1117
    end
1118

    
1119
    view.DestroyView # Destroy the view
1120

    
1121
    return networks
1122
end
1123

    
1124
def retrieve_vcenter_vms(vi_client)
1125

    
1126
    view = vi_client.vim.serviceContent.viewManager.CreateContainerView({
1127
            container: vi_client.vim.rootFolder,
1128
            type:      ['VirtualMachine'],
1129
            recursive: true
1130
    })
1131

    
1132
    pc = vi_client.vim.serviceContent.propertyCollector
1133

    
1134
    filterSpec = RbVmomi::VIM.PropertyFilterSpec(
1135
        :objectSet => [
1136
            :obj => view,
1137
            :skip => true,
1138
            :selectSet => [
1139
            RbVmomi::VIM.TraversalSpec(
1140
                :name => 'traverseEntities',
1141
                :type => 'ContainerView',
1142
                :path => 'view',
1143
                :skip => false
1144
            )
1145
            ]
1146
        ],
1147
        :propSet => [
1148
            { :type => 'VirtualMachine', :pathSet => ['name','config.template','config.uuid','config.hardware.device', 'config.extraConfig', 'datastore'] }
1149
        ]
1150
    )
1151

    
1152
    result = pc.RetrieveProperties(:specSet => [filterSpec])
1153

    
1154
    vms = {}
1155
    result.each do |r|
1156
        vms[r.obj._ref] = r.to_hash if r.obj.is_a?(RbVmomi::VIM::VirtualMachine)
1157
    end
1158

    
1159
    vmachines = {}
1160
    templates = {}
1161

    
1162
    vms.each do |ref,value|
1163
        if value["config.template"]
1164
            templates[ref] = value
1165
        else
1166
            vmachines[ref] = value
1167
        end
1168
    end
1169

    
1170
    view.DestroyView # Destroy the view
1171

    
1172
    return vmachines, templates
1173
end
1174

    
1175
def select_cluster(vc_clusters, ccr_name, vi_client)
1176
    ccr_ref = nil
1177
    STDOUT.puts("\n\e[93mWARNING: Manual intervention required!\e[39m")
1178
    STDOUT.puts("\nWhich vCenter cluster is represented by OpenNebula \e[96mhost #{ccr_name}?\e[39m\n")
1179
    STDOUT.puts
1180
    index = 0
1181
    ccr_refs = []
1182
    vc_clusters.each do |ref, ccr|
1183
        if ccr_name == ccr["name"]
1184
            item = RbVmomi::VIM::ClusterComputeResource.new(vi_client.vim, ref)
1185

    
1186
            folders = []
1187
            while !item.instance_of? RbVmomi::VIM::Datacenter
1188
                item = item.parent
1189
                if !item.instance_of?(RbVmomi::VIM::Datacenter)
1190
                    if item.name != "host"
1191
                        folders << item.name
1192
                    else
1193
                        folders << ""
1194
                    end
1195
                end
1196
                if item.nil?
1197
                    raise "Could not find the Datacenter for the host"
1198
                end
1199
            end
1200
            datacenter = item
1201
            location   = folders.reverse.join("/")
1202
            location = "/" if location.empty?
1203

    
1204
            ccr_refs << ref
1205
            STDOUT.puts("#{index+1}: #{ccr["name"]} found in #{datacenter.name} datacenter at #{location}")
1206
            index += 1
1207
        end
1208
    end
1209

    
1210
    loop do
1211
        STDOUT.print("\nFrom the list above, please \e[95mpick a number\e[39m in order to specify the cluster: ")
1212
        cluster_index = STDIN.gets.strip.to_i
1213
        next if cluster_index == 0 || cluster_index - 1 < 0 || cluster_index - 1 > ccr_refs.size
1214
        ccr_ref  = ccr_refs[cluster_index-1] rescue nil
1215
        break if ccr_ref
1216
    end
1217

    
1218
    STDOUT.puts
1219
    STDOUT.puts("-" * 80)
1220

    
1221
    ccr_ref
1222
end
1223

    
1224
################################################################################
1225
def add_new_host_attrs(vc_clusters, hpool, one_client, vcenter_ids)
1226

    
1227
    # Get all hosts from pool with VM_MAD=vcenter
1228
    hosts = hpool.retrieve_xmlelements("HOST[VM_MAD=\"vcenter\"]")
1229

    
1230
    hosts.each do |host|
1231
        begin
1232
            # Get OpenNebula host and prepare variables
1233
            one_host        = OpenNebula::Host.new_with_id(host["ID"], one_client)
1234
            rc              = one_host.info
1235
            raise rc.message if OpenNebula.is_error?(rc)
1236
            ccr_name = host["NAME"]
1237
            ccr_ref  = nil
1238
            vi_client       = VCenterDriver::VIClient.new(host["ID"])
1239
            vcenter_uuid    = vi_client.vim.serviceContent.about.instanceUuid
1240
            vcenter_version = vi_client.vim.serviceContent.about.apiVersion
1241

    
1242
            # We try to obtain the Host's moref from vCenter objects
1243
            clusters_with_name = vc_clusters[vcenter_uuid].select {|ref, ccr| ccr["name"] == ccr_name}
1244

    
1245
            # If we cannot obtain the moref we raise an exception
1246
            if clusters_with_name.size == 0
1247
                raise "Host #{ccr_name} could not be updated, cannot find cluster's MOREF"
1248
            end
1249

    
1250
            # If only one moref is found we assign the ref, if many results are
1251
            # found the administrator must select if from a list
1252
            if clusters_with_name.size == 1
1253
                ccr_ref = clusters_with_name.keys.first
1254
            else
1255
                ccr_ref = select_cluster(vc_clusters[vcenter_uuid], ccr_name, vi_client)
1256
            end
1257

    
1258
            # The host's template is updated with the new attributes
1259
            template = ""
1260
            template << "VCENTER_CCR_REF=\"#{ccr_ref}\"\n"
1261
            template << "VCENTER_INSTANCE_ID=\"#{vcenter_uuid}\"\n"
1262
            template << "VCENTER_VERSION=\"#{vcenter_version}\""
1263
            rc = one_host.update(template, true)
1264
            raise "Host #{ccr_name} could not be updated. Reason #{rc.message}" if OpenNebula.is_error?(rc)
1265
            STDOUT.puts "\nHost \e[96m#{ccr_name}\e[39m got new attributes:\n"
1266
            STDOUT.puts
1267
            STDOUT.puts "--- VCENTER_CCR_REF=#{ccr_ref}\n"
1268
            STDOUT.puts "--- VCENTER_INSTANCE_ID=#{vcenter_uuid}\n"
1269
            STDOUT.puts "--- VCENTER_VERSION=#{vcenter_version}\n"
1270
            STDOUT.puts
1271
            STDOUT.puts "-" * 80
1272
            STDOUT.puts
1273

    
1274
            # We track what hosts have been modified so we can create
1275
            # XML templates later
1276
            vcenter_ids[:host] << one_host["ID"]
1277

    
1278
        rescue Exception => e
1279
            raise e
1280
        ensure
1281
            vi_client.vim.close if vi_client
1282
        end
1283
    end
1284
end
1285

    
1286
################################################################################
1287
def create_new_clusters(vc_clusters, hpool, cpool, one_client)
1288

    
1289
    clusters = {}
1290

    
1291
    # Delete existing files from a previous script launch
1292
    ##if File.exist?("#{TEMP_DIR}/one_migrate_clusters_ids")
1293
    ##    File.delete("#{TEMP_DIR}/one_migrate_clusters_ids")
1294
    ##end
1295

    
1296
    # Get all hosts from pool with VN_MAD="vcenter"
1297
    hosts = hpool.retrieve_xmlelements("HOST[VM_MAD=\"vcenter\"]")
1298

    
1299
    hosts.each do |host|
1300
        begin
1301

    
1302
            # Get OpenNebula host and assign variables
1303
            one_host  = OpenNebula::Host.new_with_id(host["ID"], one_client)
1304
            rc   = one_host.info
1305
            raise rc.message if OpenNebula.is_error?(rc)
1306
            vi_client       = VCenterDriver::VIClient.new(host["ID"])
1307
            vcenter_uuid    = vi_client.vim.serviceContent.about.instanceUuid
1308
            ccr_name = host["NAME"]
1309

    
1310
            # Check if we find the moref for the vCenter cluster
1311
            clusters_with_name = vc_clusters[vcenter_uuid].select {|ref, ccr| ccr["name"] == ccr_name}
1312

    
1313
            if clusters_with_name.size == 0
1314
                raise "Host #{ccr_name} could not be updated, cannot find cluster's MOREF"
1315
            end
1316

    
1317
            # Check if host is assigned to a non default cluster
1318
            if host["CLUSTER_ID"] == "0"
1319

    
1320
                # Check if there's an OpenNebula cluster with the host's name
1321
                one_cluster = find_cluster_by_name(cpool, ccr_name)
1322
                if !one_cluster
1323

    
1324
                    # If the cluster doesn't exits we create a new cluster
1325
                    one_cluster = OpenNebula::Cluster.new(OpenNebula::Cluster.build_xml, one_client)
1326
                    rc = one_cluster.allocate(ccr_name)
1327
                    if OpenNebula.is_error?(rc)
1328
                        STDOUT.puts "    Error creating OpenNebula cluster you should create a cluster by hand with name #{ccr_name} before you upgrade OpenNebula: #{rc.message}\n"
1329
                        next
1330
                    end
1331
                    # We inform that the Cluster has been created
1332
                    STDOUT.puts "OpenNebula Cluster named #{ccr_name} \e[92mhas been created.\e[39m"
1333
                    STDOUT.puts
1334

    
1335
                    # Fetch the cluster info
1336
                    rc = one_cluster.info
1337
                    if OpenNebula.is_error?(rc)
1338
                        STDOUT.puts "    Error Getting information from cluster '#{ccr_name}'. Reason: #{rc.message}\n"
1339
                        next
1340
                    end
1341
                else
1342
                    STDOUT.puts "OpenNebula Cluster #{ccr_name} \e[92malready exists.\e[39m"
1343
                    STDOUT.puts
1344
                end
1345

    
1346
                # Write what cluster ID will be associated to what host ID: host_id:cluster_id
1347
                ##File.open("#{TEMP_DIR}/one_migrate_clusters_ids","a"){|f| f.puts("#{host["ID"]}:#{one_cluster["ID"]}")}
1348

    
1349
                # Store in memory the same information
1350
                clusters[host["ID"]] = one_cluster["ID"]
1351

    
1352
            else
1353
                # Write existing cluster ID
1354
                STDOUT.puts "OpenNebula Cluster #{host["CLUSTER_ID"]} \e[92malready contains Host #{ccr_name}.\e[39m"
1355
                ##File.open("#{TEMP_DIR}/one_migrate_clusters_ids","a"){|f| f.puts("#{host["ID"]}:#{host["CLUSTER_ID"]}")}
1356

    
1357
                clusters[host["ID"]] = host["CLUSTER_ID"]
1358
            end
1359

    
1360
        rescue Exception => e
1361
            raise e
1362
        ensure
1363
            vi_client.vim.close if vi_client
1364
        end
1365
    end
1366

    
1367
    return clusters
1368
end
1369

    
1370
################################################################################
1371
def prepare_host_xml_templates(host_ids, one_clusters, one_client)
1372
    host_ids.each do |host_id|
1373
        # Create XML removing old attributes
1374
        one_host = OpenNebula::Host.new_with_id(host_id, one_client)
1375
        raise rc.message if OpenNebula.is_error?(one_host)
1376
        rc   = one_host.info
1377
        raise rc.message if OpenNebula.is_error?(rc)
1378

    
1379
        # Let's see in which OpenNebula cluster we have to group this host
1380
        if !one_clusters.key?(host_id)
1381
            raise "Could not find the OpenNebula cluster ID that is going to be associated to host ID: #{host_id}"
1382
        end
1383
        cluster_id = one_clusters[host_id]
1384

    
1385
        one_cluster = OpenNebula::Cluster.new_with_id(cluster_id, one_client)
1386
        raise rc.message if OpenNebula.is_error?(one_cluster)
1387
        rc   = one_cluster.info
1388
        raise rc.message if OpenNebula.is_error?(rc)
1389

    
1390
        # We remove old attributes
1391
        xml_doc = Nokogiri::XML(one_host.to_xml, nil, "UTF-8"){|c| c.default_xml.noblanks}
1392
        xml_doc.root.xpath("TEMPLATE/PUBLIC_CLOUD").remove
1393
        xml_doc.root.xpath("TEMPLATE/VCENTER_DATASTORE").remove
1394
        xml_doc.root.xpath("TEMPLATE/RESOURCE_POOL").remove
1395

    
1396
        # We have to assign the host to the right cluster
1397
        xml_cluster_id  = xml_doc.root.at_xpath("CLUSTER_ID")
1398
        xml_cluster_id.content = cluster_id
1399
        xml_cluster  = xml_doc.root.at_xpath("CLUSTER")
1400
        xml_cluster.content = one_cluster["NAME"]
1401

    
1402
        STDOUT.puts
1403
        STDOUT.puts "New XML file #{TEMP_DIR}/one_migrate_host_#{host_id} for host \e[96m#{one_host["NAME"]}\e[39m \e[92mwas created and attributes were removed\e[39m\n"
1404
        File.open("#{TEMP_DIR}/one_migrate_host_#{host_id}","w"){|f| f.puts(xml_doc.root.to_s.gsub(/>\s*/, ">").gsub(/\s*</, "<"))}
1405
    end
1406
end
1407

    
1408
################################################################################
1409
def inspect_datastores(vc_datastores, vc_clusters, one_clusters, dspool, hpool, one_client, vcenter_ids)
1410

    
1411
    # Retrive datastores with TM_MAD="vcenter"
1412
    datastores = dspool.retrieve_xmlelements("DATASTORE[TM_MAD=\"vcenter\"]")
1413

    
1414
    # Remove previous system datastores created earlier by this script to
1415
    # avoid conflicts. Only those without VCENTER_CLUSTER attribute are removed
1416
    datastores.each do |datastore|
1417
        if datastore["TEMPLATE/TYPE"] == "SYSTEM_DS" && datastore["TEMPLATE/VCENTER_CLUSTER"].nil?
1418
            one_ds  = OpenNebula::Datastore.new_with_id(datastore["ID"], one_client)
1419
            one_ds.delete
1420
        end
1421
    end
1422

    
1423
    STDOUT.puts
1424

    
1425
    # Refresh dspool and retrieve datastores again
1426
    rc = dspool.info
1427
    raise "Datastore pool info could not be retrieved. Reason #{rc.message}" if OpenNebula.is_error?(rc)
1428

    
1429
    # Inspect existing vcenter datastores
1430
    datastores = dspool.retrieve_xmlelements("DATASTORE[TM_MAD=\"vcenter\"]")
1431
    datastores.each do |datastore|
1432
        begin
1433
            # Get OpenNebula datastore and retrieve variables
1434
            ds_id   = datastore["ID"]
1435
            one_ds  = OpenNebula::Datastore.new_with_id(datastore["ID"], one_client)
1436
            rc      = one_ds.info
1437
            raise rc.message if OpenNebula.is_error?(rc)
1438
            ds_name = one_ds["NAME"]
1439
            ccr_name = one_ds["TEMPLATE/VCENTER_CLUSTER"]
1440
            next if !ccr_name # If VCENTER_CLUSTER doesn't exist it's not usable
1441

    
1442
            # Get cluster's host from its name stored in VCENTER_CLUSTER
1443
            hosts = hpool.retrieve_xmlelements("HOST[NAME=\"#{ccr_name}\"]")
1444
            if hosts.empty?
1445
                raise "Could not find OpenNebula host associated to VCENTER_CLUSTER"
1446
            end
1447

    
1448
            # Check if host already has the ccr moref
1449
            ccr_ref = hosts.first["TEMPLATE/VCENTER_CCR_REF"]
1450
            if ccr_ref.nil?
1451
                raise "Datastore #{ds_name} could not be updated, cannot find cluster's MOREF"
1452
            end
1453

    
1454
            # Get OpenNebula host's id and create a Rbvmomi connection
1455
            host_id = hosts.first["ID"]
1456
            vi_client       = VCenterDriver::VIClient.new(host_id)
1457
            vcenter_uuid    = vi_client.vim.serviceContent.about.instanceUuid
1458
            vcenter_name    = vi_client.host
1459

    
1460
            # Datastores require now vcenter credentials
1461
            vcenter_user = hosts.first["TEMPLATE/VCENTER_USER"]
1462
            vcenter_pass = hosts.first["TEMPLATE/VCENTER_PASSWORD"]
1463
            vcenter_host = hosts.first["TEMPLATE/VCENTER_HOST"]
1464

    
1465
            # Check if we can find the datastore in objects retrieved from vCenter
1466
            datastores_with_name = vc_datastores[vcenter_uuid].select {|ref, ds| ds["name"] == ds_name}
1467
            if datastores_with_name.empty?
1468
                raise "Could not find datastore in vcenter by its name #{ds_name}"
1469
            end
1470

    
1471
            ds_ref  = nil
1472
            ds_type = nil
1473
            dc_ref  = nil
1474
            dc_name = nil
1475

    
1476
            # If we find only one vCenter datastore for that name we assign it
1477
            # otherwise the administrator should select one from the list
1478
            # We need to extract the datacenter ref and name as they are now
1479
            # required attributes.
1480
            if datastores_with_name.size == 1
1481
                vc_datastores[vcenter_uuid].each do |ref, ds|
1482
                    if ds["name"] == ds_name
1483
                        ds_ref = ref
1484
                        ds_type = ds[:ds_type]
1485

    
1486
                        item = nil
1487
                        # Check if Datastore is a StoragePod
1488
                        if ds[:ds_type] == "Datastore"
1489
                            item = RbVmomi::VIM::Datastore.new(vi_client.vim, ref)
1490
                        else
1491
                            item = RbVmomi::VIM::StoragePod.new(vi_client.vim, ref)
1492
                        end
1493

    
1494
                        # We try to get the datacenter object where this datastore is located
1495
                        while !item.instance_of? RbVmomi::VIM::Datacenter
1496
                            item = item.parent
1497
                            if item.nil?
1498
                                raise "Could not find the Datacenter for the datastore"
1499
                            end
1500
                        end
1501
                        dc_ref  = item._ref
1502
                        dc_name = item.name
1503
                        break
1504
                    end
1505
                end
1506
            else
1507
                # Select the datastore from a list of possible matches
1508
                STDOUT.puts("\n\e[93mWARNING: Manual intervention required!\e[39m")
1509
                STDOUT.puts("\nWhich vCenter datastore is represented by OpenNebula \e[96mdatastore #{ds_name}?\n\e[39m")
1510
                STDOUT.puts
1511
                index = 0
1512
                ds_info  = []
1513
                vc_datastores[vcenter_uuid].each do |ref, ds|
1514
                    if ds_name == ds["name"]
1515
                        # Discriminate if it's a datastore or a Storage Pod
1516
                        if ds[:ds_type] == "Datastore"
1517
                            item = RbVmomi::VIM::Datastore.new(vi_client.vim, ref)
1518
                        else
1519
                            item = RbVmomi::VIM::StoragePod.new(vi_client.vim, ref)
1520
                        end
1521
                        # We need the datacenter
1522
                        while !item.instance_of? RbVmomi::VIM::Datacenter
1523
                            item = item.parent
1524
                            if item.nil?
1525
                                raise "Could not find the Datacenter for the datastore"
1526
                            end
1527
                        end
1528
                        datacenter = item
1529

    
1530
                        # Prepare a hash with the information we need
1531
                        info = {}
1532
                        info[:ref]     = ref
1533
                        info[:ds_type] = ds[:ds_type]
1534
                        info[:dc_name] = datacenter.name
1535
                        info[:dc_ref] = datacenter._ref
1536
                        ds_info << info
1537
                        STDOUT.puts("#{index+1}: Datastore #{ds["name"]} in #{datacenter.name}")
1538
                        index += 1
1539
                    end
1540
                end
1541

    
1542
                # Loop until the admin user chooses the right datastore
1543
                loop do
1544
                    STDOUT.print("\nFrom the list above, please \e[95mpick one number\e[39m in order to specify the datastore: ")
1545
                    ds_index = STDIN.gets.strip.to_i
1546
                    next if ds_index == 0 || ds_index - 1 < 0 || ds_index - 1 > ds_info.size
1547
                    ds_ref  = ds_info[ds_index-1][:ref] rescue nil
1548
                    ds_type = ds_info[ds_index-1][:ds_type] rescue nil
1549
                    dc_name = ds_info[ds_index-1][:dc_name] rescue nil
1550
                    dc_ref  = ds_info[ds_index-1][:dc_ref] rescue nil
1551
                    break if ds_ref
1552
                end
1553

    
1554
                STDOUT.puts
1555
                STDOUT.puts("-" * 80)
1556
                STDOUT.puts
1557
            end
1558

    
1559
            # Raise and exception if we cannot find the datastore's moref
1560
            if ds_ref.nil?
1561
                raise "Datastore #{ds_name} could not be updated, cannot find datastore's MOREF"
1562
            end
1563

    
1564
            # Prepare new datastore attributes
1565
            template = ""
1566
            template << "VCENTER_DS_REF=\"#{ds_ref}\"\n"
1567
            template << "VCENTER_DS_NAME=\"#{ds_name}\"\n"
1568
            template << "VCENTER_DC_REF=\"#{dc_ref}\"\n"
1569
            template << "VCENTER_DC_NAME=\"#{dc_name}\"\n"
1570
            template << "VCENTER_HOST=\"#{vcenter_host}\"\n"
1571
            template << "VCENTER_USER=\"#{vcenter_user}\"\n"
1572
            template << "VCENTER_PASSWORD=\"#{vcenter_pass}\"\n"
1573
            template << "VCENTER_INSTANCE_ID=\"#{vcenter_uuid}\"\n"
1574

    
1575
            # Update the template
1576
            rc = one_ds.update(template, true)
1577
            raise "Datastore #{ds_name} could not be updated. Reason #{rc.message}" if OpenNebula.is_error?(rc)
1578

    
1579
            # Inform what attributes have been added
1580
            STDOUT.puts "Datastore \e[96m#{ds_name}\e[39m got new attributes:\n"
1581
            STDOUT.puts
1582
            STDOUT.puts "--- VCENTER_DS_REF=\"#{ds_ref}\"\n"
1583
            STDOUT.puts "--- VCENTER_DS_NAME=\"#{ds_name}\"\n"
1584
            STDOUT.puts "--- VCENTER_DC_REF=\"#{dc_ref}\"\n"
1585
            STDOUT.puts "--- VCENTER_DC_NAME=\"#{dc_name}\"\n"
1586
            STDOUT.puts "--- VCENTER_HOST=\"#{vcenter_host}\"\n"
1587
            STDOUT.puts "--- VCENTER_USER=\"#{vcenter_user}\"\n"
1588
            STDOUT.puts "--- VCENTER_PASSWORD=\"#{vcenter_pass}\"\n"
1589
            STDOUT.puts "--- VCENTER_INSTANCE_ID=\"#{vcenter_uuid}\"\n"
1590

    
1591
            STDOUT.puts
1592

    
1593
            # Update datastore information
1594
            rc = one_ds.info
1595
            raise "Datastore info #{ds_name} could not be retrieved. Reason #{rc.message}" if OpenNebula.is_error?(rc)
1596

    
1597
            # Let's see in which OpenNebula cluster we have to group this datastore
1598
            if !one_clusters.key?(host_id)
1599
                raise "Could not find the OpenNebula cluster ID that is going to be associated to host ID: #{host_id}"
1600
            end
1601
            cluster_id = one_clusters[host_id]
1602

    
1603
            # Add IMAGE datastore to OpenNebula cluster if it hasn't been assigned previously
1604
            cluster_ids = one_ds.retrieve_xmlelements("CLUSTERS")
1605
            found_cluster_ids = cluster_ids.select { |cluster| cluster["ID"] == cluster_id }
1606
            if found_cluster_ids.empty?
1607
                one_cluster  = OpenNebula::Cluster.new_with_id(cluster_id, one_client)
1608
                rc = one_cluster.adddatastore(ds_id.to_i)
1609
                if OpenNebula.is_error?(rc)
1610
                    raise "Datastore #{ds_name} could not be assigned to cluster ID: #{cluster_id} you should assign this datastore to that cluster by hand once this script finishes. Reason #{rc.message}"
1611
                else
1612
                    STDOUT.puts "Datastore \e[96m#{ds_name}\e[39m has been assigned to cluster ID: #{cluster_id}."
1613
                    STDOUT.puts
1614
                end
1615
            end
1616

    
1617
            # Check if SYSTEM datastore was not created before
1618
            # and create SYSTEM_DS associated to the existing IMAGE_DS and add it
1619
            # to the right OpenNebula cluster
1620
            if ds_type == "Datastore"
1621
                create_system_ds(ds_name, ds_ref, vcenter_name, vcenter_uuid, dc_name, dc_ref, one_client, vcenter_host, vcenter_user, vcenter_pass, cluster_id)
1622
            end
1623

    
1624
            STDOUT.puts
1625
            STDOUT.puts "-" * 80
1626
            STDOUT.puts
1627

    
1628
            # We track what datastores have been modified so we can create
1629
            # XML templates later
1630
            vcenter_ids[:ds] << one_ds["ID"]
1631

    
1632
        rescue Exception => e
1633
            raise e
1634
        ensure
1635
            vi_client.vim.close if vi_client
1636
        end
1637
    end
1638
end
1639

    
1640
################################################################################
1641
def prepare_ds_xml_templates(ds_ids, one_client)
1642

    
1643
    ds_ids.each do |ds_id|
1644
        # Create XML removing old attributes
1645
        one_ds = OpenNebula::Datastore.new_with_id(ds_id, one_client)
1646
        rc   = one_ds.info
1647
        raise rc.message if OpenNebula.is_error?(rc)
1648
        xml_doc = Nokogiri::XML(one_ds.to_xml, nil, "UTF-8"){|c| c.default_xml.noblanks}
1649
        xml_doc.root.xpath("TEMPLATE/VCENTER_CLUSTER").remove
1650

    
1651
        # Replace CLONE_TARGET from NONE to SYSTEM
1652
        xml_template = xml_doc.root.at_xpath("TEMPLATE")
1653
        clone_target = xml_template.xpath("CLONE_TARGET").text
1654
        if !clone_target.empty?
1655
            xml_template.xpath("CLONE_TARGET").remove
1656
            xml_template.add_child(xml_doc.create_element("CLONE_TARGET")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"SYSTEM"))
1657
        end
1658

    
1659
        File.open("#{TEMP_DIR}/one_migrate_ds_#{one_ds["ID"]}","w"){|f| f.puts(xml_doc.root.to_s.gsub(/>\s*/, ">").gsub(/\s*</, "<"))}
1660
        STDOUT.puts
1661
        STDOUT.puts "New XML file #{TEMP_DIR}/one_migrate_ds_#{one_ds["ID"]} for datastore \e[96m#{one_ds["NAME"]}\e[39m \e[92mwas created and attributes were removed\e[39m\n"
1662
    end
1663
end
1664

    
1665
################################################################################
1666
def inspect_networks(vc_networks, vc_clusters, one_clusters, vnpool, hpool, one_client, vcenter_ids)
1667

    
1668
    # Retrive virtual networks that have the VCENTER_TYPE attribute
1669
    vnets = vnpool.retrieve_xmlelements("VNET[TEMPLATE/VCENTER_TYPE=\"Port Group\" or TEMPLATE/VCENTER_TYPE=\"Distributed Port Group\"]")
1670

    
1671
    # For each port group...
1672
    vnets.each do |vnet|
1673

    
1674
        begin
1675
            # Get OpenNebula's virtual network and retrieve some variables
1676
            one_vnet = OpenNebula::VirtualNetwork.new_with_id(vnet["ID"], one_client)
1677
            rc   = one_vnet.info
1678
            raise rc.message if OpenNebula.is_error?(rc)
1679
            vnet_id      = vnet["ID"]
1680
            vnet_name    = vnet["NAME"]
1681
            vnet_bridge  = vnet["TEMPLATE/BRIDGE"]
1682
            vnet_pg_type = vnet["TEMPLATE/VCENTER_TYPE"]
1683

    
1684
            # Try to get cluster associated to this network from imported name
1685
            # OpenNebula creates a vnet name with portgroup name - cluster name
1686
            ccr_name = vnet_name.split(" - ")[-1] rescue nil
1687

    
1688
            # Let's see if we can find the cluster name from the vnet name
1689
            if ccr_name
1690
                hosts = hpool.retrieve_xmlelements("HOST[NAME=\"#{ccr_name}\"]")
1691
                ccr_name = nil if hosts.empty?
1692
            end
1693

    
1694
            # If cluster name could not be determined, user action required
1695
            # The administrator must choose what cluster is associate with the vnet
1696
            if !ccr_name
1697
                STDOUT.puts("\n\e[93mWARNING: Manual intervention required!\e[39m")
1698
                STDOUT.puts("\nWhich vCenter cluster is associated with OpenNebula \e[96mvnet #{vnet_name}?\n\e[39m")
1699

    
1700
                ccr_names = []
1701
                hpool.each_with_index do |host, index|
1702
                    STDOUT.puts("#{index+1}: #{host["NAME"]} in #{host["TEMPLATE/VCENTER_HOST"]}")
1703
                    ccr_names << host["NAME"]
1704
                end
1705

    
1706
                STDOUT.puts("#{ccr_names.size+1}: None of the above.")
1707

    
1708
                loop do
1709
                    STDOUT.print("\nFrom the list above, please pick one number in order to specify the cluster: ")
1710
                    cluster_index = STDIN.gets.strip.to_i
1711
                    next if cluster_index == 0 || cluster_index - 1 < 0 || cluster_index - 1 > ccr_names.size+1
1712
                    ccr_name  = ccr_names[cluster_index-1] rescue nil
1713
                    break
1714
                end
1715

    
1716
                STDOUT.puts
1717
                STDOUT.puts("-" * 80)
1718
                STDOUT.puts
1719
            end
1720

    
1721
            # If we could not determine what cluster name is associated to the network
1722
            # raise an exception.
1723
            if !ccr_name
1724
                raise "We could not find the host name associated to vnet #{vnet_name}\n"\
1725
                      "You may have to import the vCenter cluster using the onevcenter tool."
1726
            end
1727

    
1728
            # Get cluster's ccr ref and host id from its name
1729
            hosts = hpool.retrieve_xmlelements("HOST[NAME=\"#{ccr_name}\"]")
1730
            ccr_ref = hosts.first["TEMPLATE/VCENTER_CCR_REF"]
1731
            if ccr_ref.nil?
1732
                raise "Vnet #{vnet_name} could not be updated, cannot find cluster's MOREF"
1733
            end
1734
            host_id      = hosts.first["ID"]
1735
            vi_client    = VCenterDriver::VIClient.new(host_id)
1736
            vcenter_uuid = vi_client.vim.serviceContent.about.instanceUuid
1737

    
1738
            # Get vnet MOREF from vcenter info and the port group's name
1739
            vnets_with_name = vc_networks[vcenter_uuid].select {|ref, net| net["name"] == vnet_bridge}
1740
            if vnets_with_name.empty?
1741
                raise "Could not find vnet in vcenter by its name #{vnet_name}"
1742
            end
1743

    
1744
            # Check how many refs we've found for that port group name
1745
            vnet_ref  = nil
1746

    
1747
            if vnets_with_name.size == 1
1748
                vnet_ref = vnets_with_name.keys.first
1749
            else
1750
                # If there are many possible morefs the admin must select one from a list
1751
                STDOUT.puts("\n\e[93mWARNING: Manual intervention required!\e[39m")
1752
                STDOUT.puts("\nWhich vCenter port group is represented by OpenNebula \e[96mvnet #{vnet_name}?\e[39m\n")
1753
                STDOUT.puts
1754
                index = 0
1755
                vnet_refs  = []
1756
                vc_networks[vcenter_uuid].each do |ref, net|
1757
                    if net["name"] == vnet_bridge
1758
                        item = RbVmomi::VIM::Network.new(vi_client.vim, ref)
1759

    
1760
                        # We need the datatacenter info associated to the port group
1761
                        while !item.instance_of? RbVmomi::VIM::Datacenter
1762
                            item = item.parent
1763
                            if item.nil?
1764
                                raise "Could not find the Datacenter for the datastore"
1765
                            end
1766
                        end
1767
                        datacenter = item
1768

    
1769
                        vnet_refs << ref
1770
                        STDOUT.puts("#{index+1}: Virtual Network #{vnet_name} in #{datacenter.name}")
1771
                        index += 1
1772
                    end
1773
                end
1774

    
1775
                # Loop until the administrator selects a vnet
1776
                loop do
1777
                    STDOUT.print("\nFrom the list above, please \e[95mpick one number\e[39m in order to specify the vnet: ")
1778
                    vnet_index = STDIN.gets.strip.to_i
1779
                    next if vnet_index == 0 || vnet_index - 1 < 0 || vnet_index - 1 > vnet_refs.size
1780
                    vnet_ref  = vnet_refs[vnet_index-1] rescue nil
1781
                    break if vnet_ref
1782
                end
1783

    
1784
                STDOUT.puts
1785
                STDOUT.puts("-" * 80)
1786
            end
1787

    
1788
            STDOUT.puts
1789

    
1790
            raise "Vnet #{vnet_name} could not be updated, cannot find vnet's MOREF" if vnet_ref.nil?
1791

    
1792
            # Prepare vnet's template attributes
1793
            template = ""
1794
            template << "VCENTER_NET_REF=\"#{vnet_ref}\"\n"
1795
            template << "VCENTER_INSTANCE_ID=\"#{vcenter_uuid}\"\n"
1796
            template << "VCENTER_PORTGROUP_TYPE=\"#{vnet_pg_type}\"\n"
1797
            template << "VCENTER_CCR_REF=\"#{ccr_ref}\"\n"
1798

    
1799
            # Try to update the vnet template
1800
            rc = one_vnet.update(template, true)
1801
            raise "Vnet #{vnet_name} could not be updated. Reason #{rc.message}" if OpenNebula.is_error?(rc)
1802

    
1803
            # Inform what attributes have been added
1804
            STDOUT.puts "\nVnet \e[96m#{vnet_name}\e[39m got new attributes:\n"
1805
            STDOUT.puts "--- VCENTER_NET_REF=#{vnet_ref}\n"
1806
            STDOUT.puts "--- VCENTER_INSTANCE_ID=#{vcenter_uuid}\n"
1807
            STDOUT.puts "--- VCENTER_PORTGROUP_TYPE=#{vnet_pg_type}\n"
1808
            STDOUT.puts "--- VCENTER_CCR_REF=#{ccr_ref}\n"
1809
            STDOUT.puts
1810

    
1811
            # Let's see in which OpenNebula cluster we have to group this vnet
1812
            if !one_clusters.key?(host_id)
1813
                raise "Could not find the OpenNebula cluster ID that is going to be associated to host ID: #{host_id}"
1814
            end
1815

    
1816
            # Add vnet to OpenNebula cluster
1817
            # Check if the vnet is already assigned to the right cluster
1818
            cluster_id        = one_clusters[host_id]
1819
            cluster_ids       = one_vnet.retrieve_xmlelements("CLUSTERS")
1820
            found_cluster_ids = cluster_ids.select { |cluster| cluster["ID"] == cluster_id }
1821

    
1822
            if found_cluster_ids.empty?
1823
                one_cluster  = OpenNebula::Cluster.new_with_id(cluster_id, one_client)
1824
                rc = one_cluster.addvnet(vnet_id.to_i)
1825
                if OpenNebula.is_error?(rc)
1826
                    raise "Network #{vnet_name} could not be assigned to cluster ID: #{cluster_id} you should assign this virtual network to that cluster by hand once this script finishes. Reason #{rc.message}"
1827
                else
1828
                    STDOUT.puts "Vnet \e[96m#{vnet_name}\e[39m has been assigned to cluster ID: #{cluster_id}."
1829
                end
1830
            end
1831

    
1832
            STDOUT.puts
1833
            STDOUT.puts "-" * 80
1834
            STDOUT.puts
1835

    
1836
            # We track what vcenter_ids have been modified so we can create
1837
            # XML templates later
1838
            vcenter_ids[:vnet] << one_vnet["ID"]
1839

    
1840
        rescue Exception => e
1841
            raise e
1842
        ensure
1843
            vi_client.vim.close if vi_client
1844
        end
1845
    end
1846
end
1847

    
1848
################################################################################
1849
def prepare_vnet_xml_templates(vnet_ids, one_client)
1850
    vnet_ids.each do |vnet_id|
1851
        # Create XML removing old attributes
1852
        one_vnet = OpenNebula::VirtualNetwork.new_with_id(vnet_id, one_client)
1853
        rc   = one_vnet.info
1854
        raise rc.message if OpenNebula.is_error?(rc)
1855
        xml_doc = Nokogiri::XML(one_vnet.to_xml, nil, "UTF-8"){|c| c.default_xml.noblanks}
1856
        xml_doc.root.xpath("TEMPLATE/VCENTER_TYPE").remove
1857
        File.open("#{TEMP_DIR}/one_migrate_vnet_#{vnet_id}","w"){|f| f.puts(xml_doc.root.to_s.gsub(/>\s*/, ">").gsub(/\s*</, "<"))}
1858
        STDOUT.puts
1859
        STDOUT.puts "New XML file #{TEMP_DIR}/one_migrate_vnet_#{vnet_id} for vnet \e[96m#{one_vnet["NAME"]}\e[39m \e[92mwas created and attributes were removed\e[39m\n"
1860
    end
1861
end
1862

    
1863
################################################################################
1864
def add_new_image_attrs(ipool, one_client, vcenter_ids)
1865

    
1866
    # Retrieve images with VCENTER_IMPORTED="YES"
1867
    imported_images = ipool.retrieve_xmlelements("IMAGE[TEMPLATE/VCENTER_IMPORTED=\"YES\"]")
1868

    
1869
    # Remove previous imported images so we regenerate them again
1870
    imported_images.each do |image|
1871
        one_image  = OpenNebula::Image.new_with_id(image["ID"], one_client)
1872
        one_image.delete
1873

    
1874
        loop do
1875
            rc = one_image.info
1876
            break if OpenNebula.is_error?(rc)
1877
        end
1878
    end
1879

    
1880
    STDOUT.puts
1881

    
1882
    # Refresh pool
1883
    rc = ipool.info_all
1884
    raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
1885

    
1886
    # Loop through existing images
1887
    ipool.each do |image|
1888
        # Initialize some variables
1889
        image_name = image["NAME"]
1890
        template = ""
1891
        adapter_type = nil
1892
        disk_type    = nil
1893

    
1894
        # Get images
1895
        one_image = OpenNebula::Image.new_with_id(image["ID"], one_client)
1896
        rc   = one_image.info
1897
        raise rc.message if OpenNebula.is_error?(rc)
1898

    
1899
        # Create VCENTER_ADAPTER_TYPE attribute
1900
        adapter_type = one_image["TEMPLATE/ADAPTER_TYPE"]
1901
        template << "VCENTER_ADAPTER_TYPE=\"#{adapter_type}\"\n" if adapter_type
1902

    
1903
        # Check if DISK_TYPE is one of those used by vcenter as this attribute
1904
        # is shared with KVM images
1905
        disk_type = one_image["TEMPLATE/DISK_TYPE"]
1906
        disk_types = ["delta","eagerZeroedThick","flatMonolithic",
1907
                      "preallocated","raw","rdm","rdmp","seSparse",
1908
                      "sparse2Gb","sparseMonolithic","thick","thick2Gb","thin"]
1909
        if disk_type && disk_types.include?(disk_type)
1910
            template << "VCENTER_DISK_TYPE=\"#{disk_type}\"\n"
1911
        end
1912

    
1913
        # Update image's template
1914
        if !template.empty?
1915
            rc = one_image.update(template, true)
1916
            raise "Image #{image_name} could not be updated. Reason #{rc.message}" if OpenNebula.is_error?(rc)
1917

    
1918
            # Inform about what attributes have been added
1919
            STDOUT.puts "\nImage \e[96m#{image_name}\e[39m got new attributes:\n"
1920
            STDOUT.puts
1921
            STDOUT.puts "--- VCENTER_DISK_TYPE=#{disk_type}\n"
1922
            STDOUT.puts "--- VCENTER_ADAPTER_TYPE=#{adapter_type}\n"
1923
            STDOUT.puts
1924

    
1925
            STDOUT.puts
1926
            STDOUT.puts "-" * 80
1927
            STDOUT.puts
1928

    
1929
            # We track what vcenter_ids have been modified so we can create
1930
            # XML templates later
1931
            vcenter_ids[:image] << one_image["ID"]
1932
        end
1933
    end
1934
end
1935

    
1936
################################################################################
1937
def prepare_image_xml_templates(image_ids, hpool, one_client)
1938
    image_ids.each do |image_id|
1939
        begin
1940
            one_image = OpenNebula::Image.new_with_id(image_id, one_client)
1941
            rc   = one_image.info
1942
            raise rc.message if OpenNebula.is_error?(rc)
1943

    
1944
            # Remove old attributes
1945
            xml_doc = Nokogiri::XML(one_image.to_xml, nil, "UTF-8"){|c| c.default_xml.noblanks}
1946
            xml_doc.root.xpath("TEMPLATE/ADAPTER_TYPE").remove
1947
            xml_doc.root.xpath("TEMPLATE/DISK_TYPE").remove
1948

    
1949
            # Update image's size
1950
            one_ds = OpenNebula::Datastore.new_with_id(one_image["DATASTORE_ID"], one_client)
1951
            rc   = one_ds.info
1952
            raise rc.message if OpenNebula.is_error?(rc)
1953
            image_source = one_image["SOURCE"]
1954
            ds_ref  = one_ds["TEMPLATE/VCENTER_DS_REF"]
1955

    
1956
            # Get Datastore's cluster name
1957
            ccr_name = one_ds["TEMPLATE/VCENTER_CLUSTER"]
1958
            next if !ccr_name
1959

    
1960
            # Get cluster's host from its name
1961
            hosts = hpool.retrieve_xmlelements("HOST[NAME=\"#{ccr_name}\"]")
1962
            if hosts.empty?
1963
                raise "Could not find OpenNebula host associated to VCENTER_CLUSTER"
1964
            end
1965
            host_id = hosts.first["ID"]
1966

    
1967
            vi_client = VCenterDriver::VIClient.new(host_id)
1968
            disk_size = get_image_size(RbVmomi::VIM::Datastore.new(vi_client.vim, ds_ref), image_source)
1969
            xml_size  = xml_doc.root.at_xpath("SIZE")
1970
            xml_size.content = disk_size
1971

    
1972
            File.open("#{TEMP_DIR}/one_migrate_image_#{image_id}","w"){|f| f.puts(xml_doc.root.to_s.gsub(/>\s*/, ">").gsub(/\s*</, "<"))}
1973

    
1974
            STDOUT.puts
1975
            STDOUT.puts "New XML file #{TEMP_DIR}/one_migrate_image_#{image_id} for image \e[96m#{one_image["NAME"]}\e[39m \e[92mwas created and attributes were removed\e[39m\n"
1976

    
1977
        rescue Exception => e
1978
            raise e
1979
        ensure
1980
            vi_client.vim.close if vi_client
1981
        end
1982
    end
1983
end
1984

    
1985
################################################################################
1986
def inspect_templates(vc_templates, vc_clusters, one_clusters, tpool, ipool, vnpool, dspool, hpool, one_client, vcenter_ids)
1987
    # Retrieve all OpenNebula templates associated with PUBLIC_CLOUD=vcenter
1988
    templates = tpool.retrieve_xmlelements("VMTEMPLATE[TEMPLATE/PUBLIC_CLOUD/TYPE=\"vcenter\"]")
1989

    
1990
    templates.each do |template|
1991
        begin
1992
            # Refresh pools
1993
            rc = vnpool.info_all
1994
            raise "\n    ERROR! Could not update vnpool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
1995
            rc = ipool.info_all
1996
            raise "\n    ERROR! Could not update ipool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
1997
            rc = dspool.info
1998
            raise "\n    ERROR! Could not update dspool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
1999

    
2000
            # Get some variables
2001
            ccr_ref      = nil
2002
            vcenter_uuid = nil
2003
            host_id      = nil
2004

    
2005
            template_name    = template["NAME"]
2006
            template_uuid    = template["TEMPLATE/PUBLIC_CLOUD/VM_TEMPLATE"]
2007
            template_ref     = template["TEMPLATE/PUBLIC_CLOUD/VCENTER_REF"]
2008
            template_cluster = template["TEMPLATE/PUBLIC_CLOUD/HOST"]
2009
            template_rp      = template["TEMPLATE/RESOURCE_POOL"]
2010
            template_user_rp = template["TEMPLATE/USER_INPUTS/RESOURCE_POOL"]
2011

    
2012
            # Check if we can find what vCenter cluster is associated with the
2013
            # Template
2014
            if template_cluster
2015
                # Does a host with the template host name exist?
2016
                hosts = hpool.retrieve_xmlelements("HOST[NAME=\"#{template_cluster}\"]")
2017
                if hosts.empty?
2018
                    template_cluster = nil
2019
                else
2020
                    # Check if we can get the morefs from the OpenNebula host
2021
                    ccr_ref      = hosts.first["TEMPLATE/VCENTER_CCR_REF"]
2022
                    vcenter_uuid = hosts.first["TEMPLATE/VCENTER_INSTANCE_ID"]
2023
                    host_id      = hosts.first["ID"]
2024
                    vcenter_user = hosts.first["TEMPLATE/VCENTER_USER"]
2025
                    vcenter_pass = hosts.first["TEMPLATE/VCENTER_PASSWORD"]
2026
                    vcenter_host = hosts.first["TEMPLATE/VCENTER_HOST"]
2027

    
2028
                    template_cluster = nil if !ccr_ref || !vcenter_uuid
2029
                end
2030
            end
2031

    
2032
            # As we don't know which vCenter cluster is associated with the template
2033
            # The administrator must select one from a list
2034
            if !template_cluster
2035
                hpool_vcenter = hpool.select{|h| h["VM_MAD"] == "vcenter"}
2036

    
2037
                if hpool_vcenter.count == 1
2038
                    template_cluster = hpool_vcenter.first["NAME"]
2039
                else
2040
                    STDOUT.puts("\n\e[93mWARNING: Manual intervention required!\e[39m")
2041
                    STDOUT.puts("\nWhich vCenter cluster is associated with OpenNebula \e[96mtemplate #{template_name}?\n\e[39m")
2042
                    STDOUT.puts
2043

    
2044
                    ccr_names = []
2045
                    hpool_vcenter.each_with_index do |host, index|
2046
                        STDOUT.puts("#{index+1}: #{host["NAME"]} in #{host["TEMPLATE/VCENTER_HOST"]}")
2047
                        ccr_names << host["NAME"]
2048
                    end
2049

    
2050
                    STDOUT.puts("#{ccr_names.size+1}: None of the above.")
2051

    
2052
                    loop do
2053
                        STDOUT.print("\nFrom the list above, please \e[95mpick one number\e[39m in order to specify the cluster: ")
2054
                        cluster_index = STDIN.gets.strip.to_i
2055
                        next if cluster_index == 0 || cluster_index - 1 < 0 || cluster_index > ccr_names.size+1
2056
                        template_cluster  = ccr_names[cluster_index-1] rescue nil
2057
                        break
2058
                    end
2059

    
2060
                    STDOUT.puts
2061
                    STDOUT.puts "-" * 80
2062
                    STDOUT.puts
2063
                end
2064

    
2065
                if !template_cluster
2066
                    raise "We could not find the host name associated to template #{template_name}\n"\
2067
                          "You may have to import the OpenNebula host first using onevcenter tool."
2068
                end
2069

    
2070
                # Get host attributes from the name of the cluster associated
2071
                # to the template
2072
                hosts = hpool.retrieve_xmlelements("HOST[NAME=\"#{template_cluster}\"]")
2073

    
2074
                ccr_ref      = hosts.first["TEMPLATE/VCENTER_CCR_REF"]
2075
                vcenter_uuid = hosts.first["TEMPLATE/VCENTER_INSTANCE_ID"]
2076
                host_id      = hosts.first["ID"]
2077
                vcenter_user = hosts.first["TEMPLATE/VCENTER_USER"]
2078
                vcenter_pass = hosts.first["TEMPLATE/VCENTER_PASSWORD"]
2079
                vcenter_host = hosts.first["TEMPLATE/VCENTER_HOST"]
2080
            end
2081

    
2082
            if ccr_ref.nil? || vcenter_uuid.nil?
2083
                raise "Template #{template_name} could not be updated, cannot find cluster's MOREF: '#{ccr_ref}'" \
2084
                      ", or vcenter uuid: '#{vcenter_uuid}'. "
2085
            end
2086

    
2087
            # Create Rbvmomi connection
2088
            vi_client    = VCenterDriver::VIClient.new(host_id)
2089
            vcenter_uuid = vi_client.vim.serviceContent.about.instanceUuid
2090

    
2091
            # We try to check if the template's moref found inside the XML template
2092
            # is found in the templates retrieved from vcenter only once
2093
            if template_ref
2094

    
2095
                templates_found = vc_templates[vcenter_uuid].select do |ref, value|
2096
                    template_ref == ref
2097
                end
2098

    
2099
                if templates_found.size != 1
2100
                    template_ref = nil
2101
                end
2102
            end
2103

    
2104
            # Try to get moref using the templates uuid note that that uuid
2105
            # is not unique
2106
            if !template_ref && template_uuid
2107
                templates_found = 0
2108
                vc_templates[vcenter_uuid].each do |ref, value|
2109
                    if value["config.uuid"] == template_uuid
2110
                        templates_found += 1
2111
                        if templates_found > 1
2112
                            template_ref = nil
2113
                        else
2114
                            template_ref = ref
2115
                        end
2116
                    end
2117
                end
2118
            end
2119

    
2120
            # If we could not found the template moref the administrator has to help us
2121
            if !template_ref
2122
                STDOUT.puts("\n\e[93mWARNING: Manual intervention required!\e[39m")
2123
                STDOUT.puts("\nWhich vCenter template is associated with OpenNebula \e[96mtemplate #{template_name}?\n\e[39m")
2124
                STDOUT.puts
2125
                index = 0
2126
                template_refs  = []
2127

    
2128
                vc_templates[vcenter_uuid].each do |ref, t|
2129
                    item = RbVmomi::VIM::VirtualMachine.new(vi_client.vim, ref)
2130

    
2131
                    folders = []
2132
                    while !item.instance_of? RbVmomi::VIM::Datacenter
2133
                        item = item.parent
2134
                        if !item.instance_of?(RbVmomi::VIM::Datacenter)
2135
                            if item.name != "vm"
2136
                                folders << item.name
2137
                            else
2138
                                folders << ""
2139
                            end
2140
                        end
2141
                        if item.nil?
2142
                            raise "Could not find the Datacenter for the template"
2143
                        end
2144
                    end
2145
                    datacenter = item
2146
                    location   = folders.reverse.join("/")
2147
                    location = "/" if location.empty?
2148

    
2149
                    template_refs << ref
2150
                    STDOUT.puts("#{index+1}: Template #{t["name"]} in #{datacenter.name} Location: #{location}")
2151
                    index += 1
2152
                end
2153

    
2154
                STDOUT.puts("#{template_refs.size+1}: None of the above.")
2155

    
2156
                loop do
2157
                    STDOUT.print("\nFrom the list above, please \e[95mpick a number\e[39m in order to specify the template: ")
2158
                    template_index = STDIN.gets.strip.to_i
2159
                    next if template_index == 0 || template_index - 1 < 0 || template_index > template_refs.size + 1
2160
                    template_ref  = template_refs[template_index-1] rescue nil
2161
                    break
2162
                end
2163

    
2164
                STDOUT.puts
2165
                STDOUT.puts "-" * 80
2166
            end
2167

    
2168
            STDOUT.puts
2169

    
2170
            if !template_ref
2171
                raise "Template #{template_name} could not be updated, cannot find template's MOREF"
2172
            end
2173

    
2174
            # Get OpenNebulas's template
2175
            one_template = OpenNebula::Template.new_with_id(template["ID"], one_client)
2176
            rc   = one_template.info
2177
            raise "Could not get info for template #{template["ID"]}. Reason: #{rc.message}" if OpenNebula.is_error?(rc)
2178

    
2179
            # Find vcenter template in vc_templates
2180
            vc_template  = nil
2181
            vc_template_object = nil
2182
            vc_templates[vcenter_uuid].each do |ref, value|
2183
                if ref == template_ref
2184
                    vc_template = value
2185
                    vc_template_object = RbVmomi::VIM::VirtualMachine.new(vi_client.vim, template_ref)
2186
                    break
2187
                end
2188
            end
2189

    
2190
            if vc_template.nil?
2191
                raise "Template #{template_name} could not be updated, cannot find vcenter info for this template"
2192
            end
2193

    
2194
            # Migrate USER_INPUTS/RESOURCE_POOL to USER_INPUTS/VCENTER_RESOURCE_POOL
2195
            user_inputs = nil
2196
            if template_user_rp
2197
                inputs = template.retrieve_xmlelements("TEMPLATE/USER_INPUTS").first.to_hash["USER_INPUTS"] rescue nil
2198
                if inputs
2199
                    user_inputs = ""
2200
                    user_inputs << "USER_INPUTS=["
2201
                    inputs.each do |key, value|
2202
                        user_inputs << "#{key}=\"#{value}\",\n"
2203
                    end
2204
                    user_inputs << "VCENTER_RESOURCE_POOL=\"#{template_user_rp}\"\n"
2205
                    user_inputs << "]"
2206
                end
2207
            end
2208

    
2209
            # Prepare VM template with new attributes
2210
            template = ""
2211
            template << "VCENTER_TEMPLATE_REF=\"#{template_ref}\"\n"
2212
            template << "VCENTER_INSTANCE_ID=\"#{vcenter_uuid}\"\n"
2213
            template << "VCENTER_CCR_REF=\"#{ccr_ref}\"\n"
2214
            template << "VCENTER_RESOURCE_POOL=\"#{template_rp}\"\n" if template_rp
2215
            template << user_inputs if template_user_rp
2216

    
2217
            # Try to update VM template
2218
            rc = one_template.update(template, true)
2219
            raise "Template #{template_name} could not be updated. Reason #{rc.message}" if OpenNebula.is_error?(rc)
2220

    
2221
            # Inform what attributes have been added
2222
            STDOUT.puts "\nTemplate \e[96m#{template_name}:\e[39m"
2223
            STDOUT.puts "--- New attribute VCENTER_TEMPLATE_REF=#{template_ref} added\n"
2224
            STDOUT.puts "--- New attribute VCENTER_INSTANCE_ID=#{vcenter_uuid} added\n"
2225
            STDOUT.puts "--- New attribute VCENTER_CCR_REF=#{ccr_ref} added\n"
2226
            STDOUT.puts "--- New attribute VCENTER_RESOURCE_POOL=#{template_rp} added\n" if template_rp
2227
            STDOUT.puts "--- New attribute USER_INPUTS/VCENTER_RESOURCE_POOL=#{template_user_rp} added\n" if template_user_rp
2228

    
2229
            # Prepare template for migration
2230
            xml_doc           = Nokogiri::XML(one_template.to_xml, nil, "UTF-8"){|c| c.default_xml.noblanks}
2231
            xml_template      = xml_doc.root.at_xpath("TEMPLATE")
2232
            # Retrieve and remove existing disks
2233
            existing_disks    = xml_doc.root.xpath("TEMPLATE/DISK").remove
2234
            # Retrieve and remove existing nics
2235
            existing_nics     = xml_doc.root.xpath("TEMPLATE/NIC").remove
2236
            template_id       = one_template["ID"]
2237

    
2238
            # Discover existing disks and/or nics. Note that datastores will
2239
            # be created if the images require them.
2240
            dc = get_dc(vc_template_object)
2241
            dc_name = dc.name
2242
            dc_ref  = dc._ref
2243
            vcenter_name = vi_client.host
2244
            STDOUT.puts "--- Discovering disks and network interfaces inside the template (please be patient)"
2245
            unmanaged = template_unmanaged_discover(vc_template["config.hardware.device"],
2246
                                                    template_cluster,
2247
                                                    ccr_ref,
2248
                                                    vcenter_name,
2249
                                                    vcenter_uuid,
2250
                                                    vcenter_user,
2251
                                                    vcenter_pass,
2252
                                                    vcenter_host,
2253
                                                    dc_name,
2254
                                                    dc_ref,
2255
                                                    ipool,
2256
                                                    vnpool,
2257
                                                    dspool,
2258
                                                    hpool,
2259
                                                    one_client,
2260
                                                    template_ref,
2261
                                                    vc_template["name"],
2262
                                                    template_id,
2263
                                                    one_clusters,
2264
                                                    vcenter_ids)
2265

    
2266
            if !unmanaged[:images].empty?
2267
                STDOUT.puts "--- Adding DISK elements for discovered disks to new XML"
2268
                # Create images for unmanaged disks
2269
                unmanaged[:images].each do |image_id|
2270
                    # Add existing disk to xml
2271
                    disk = xml_template.add_child(xml_doc.create_element("DISK"))
2272
                    disk.add_child(xml_doc.create_element("IMAGE_ID")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{image_id}"))
2273
                    disk.add_child(xml_doc.create_element("OPENNEBULA_MANAGED")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"NO"))
2274
                end
2275
            end
2276

    
2277
            # Add managed disks after unmanaged disks
2278
            xml_template.add_child(existing_disks)
2279

    
2280
            if !unmanaged[:networks].empty?
2281
                STDOUT.puts "--- Adding NIC elements for discovered nics to new XML"
2282
                # Create networks for unmanaged nics
2283
                unmanaged[:networks].each do |network_id|
2284
                    # Add existing nics to xml
2285
                    nic = xml_template.add_child(xml_doc.create_element("NIC"))
2286
                    nic.add_child(xml_doc.create_element("NETWORK_ID")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{network_id}"))
2287
                    nic.add_child(xml_doc.create_element("OPENNEBULA_MANAGED")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"NO"))
2288
                end
2289
            else
2290
                STDOUT.puts "--- All discovered networks are ready to use"
2291
            end
2292

    
2293
            # Add managed disks after unmanaged disks
2294
            xml_template.add_child(existing_nics)
2295

    
2296
            # Remove SCHED_REQUIREMENTS from TEMPLATE
2297
            xml_template.xpath("SCHED_REQUIREMENTS").remove
2298

    
2299
            # Remove KEEP_DISKS_ON_DONE from TEMPLATE
2300
            xml_template.xpath("KEEP_DISKS_ON_DONE").remove
2301

    
2302
            # Remove PUBLIC_CLOUD section from TEMPLATE
2303
            xml_template.xpath("PUBLIC_CLOUD").remove
2304

    
2305
            # Remove RESOURCE_POOL section from TEMPLATE and USER_INPUTS
2306
            xml_template.xpath("RESOURCE_POOL").remove
2307
            xml_template.xpath("USER_INPUTS/RESOURCE_POOL").remove
2308

    
2309
            # Remove VCENTER_DATASTORE section from TEMPLATE and USER_INPUTS
2310
            vcenter_datastore = xml_template.xpath("VCENTER_DATASTORE").text
2311
            xml_template.xpath("VCENTER_DATASTORE").remove
2312
            xml_template.xpath("USER_INPUTS/VCENTER_DATASTORE").remove
2313

    
2314
            # Replace CUSTOMIZATION_SPEC with VCENTER_CUSTOMIZATION_SPEC
2315
            customization_spec = xml_template.xpath("CUSTOMIZATION_SPEC").text
2316
            if !customization_spec.empty?
2317
                xml_template.xpath("CUSTOMIZATION_SPEC").remove
2318
                xml_template.add_child(xml_doc.create_element("VCENTER_CUSTOMIZATION_SPEC")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"#{customization_spec}"))
2319
            end
2320

    
2321
            # Remove SCHED_DS_REQUIREMENTS, OpenNebula will choose datastores
2322
            # using the clusters associated to datastores
2323
            xml_template.xpath("SCHED_DS_REQUIREMENTS").remove
2324

    
2325
            # If vcenter_datastore is a SYSTEM_DS then it's a storage pod
2326
            # Let's create a SCHED_DS_REQUIREMENTS to force using that datastore
2327
            ds_id = nil
2328
            if !vcenter_datastore.empty?
2329
                ds = find_datastore_by_name(dspool, "#{vcenter_datastore}")
2330
                ds_id   = ds["ID"]
2331
                ds_type = ds["TEMPLATE/TYPE"]
2332
                if ds_type == "SYSTEM_DS"
2333
                    sched_ds_req = one_template["TEMPLATE/SCHED_DS_REQUIREMENTS"]
2334

    
2335
                    if sched_ds_req
2336
                        xml_template.xpath("SCHED_DS_REQUIREMENTS").remove
2337
                        requirements = "ID=#{ds_id} & (#{sched_ds_req})"
2338
                        xml_template.add_child(xml_doc.create_element("SCHED_DS_REQUIREMENTS")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"\"#{requirements}\""))
2339
                    else
2340
                        # Add a SCHED_DS_REQUIREMENTS to template
2341
                        xml_template.add_child(xml_doc.create_element("SCHED_DS_REQUIREMENTS")).add_child(Nokogiri::XML::CDATA.new(xml_doc,"\"ID=#{ds_id}\""))
2342
                    end
2343
                end
2344
            end
2345

    
2346
            #Write new XML template to file
2347
            xml_doc = xml_doc.root.to_s.gsub(/>\s*/, ">").gsub(/\s*</, "<")
2348
            File.open("#{TEMP_DIR}/one_migrate_template_#{template_id}","w"){|f| f.puts(xml_doc)}
2349
            STDOUT.puts "--- New XML file #{TEMP_DIR}/one_migrate_template_#{template_id} for template \e[96m#{template_name}\e[39m \e[92mwas created and attributes were removed\e[39m\n"
2350
            STDOUT.puts
2351
            STDOUT.puts "-" * 80
2352
            STDOUT.puts
2353

    
2354
        rescue Exception => e
2355
            raise e
2356
        ensure
2357
            vi_client.vim.close if vi_client
2358
        end
2359
    end
2360
end
2361

    
2362
################################################################################
2363
def inspect_vms(vc_vmachines, vc_templates, vc_clusters, one_clusters, vmpool, ipool, tpool, vnpool, dspool, hpool, one_client, vcenter_ids)
2364

    
2365
    # Retrieve vCenter deployed or importer VMs
2366
    vms = vmpool.retrieve_xmlelements("VM[USER_TEMPLATE/PUBLIC_CLOUD/TYPE=\"vcenter\"]")
2367

    
2368
    vms.each do |vm|
2369
        next if !vm["DEPLOY_ID"] # Ignore undeployed vms
2370

    
2371
        begin
2372
            # Refresh pools
2373
            rc = vnpool.info_all
2374
            raise "\n    ERROR! Could not update vnpool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
2375
            rc = ipool.info_all
2376
            raise "\n    ERROR! Could not update ipool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
2377
            rc = dspool.info
2378
            raise "\n    ERROR! Could not update dspool. Reason #{rc.message}" if OpenNebula.is_error?(rc)
2379

    
2380
            # Find vcenter VM in vc_vmachines
2381
            vm_name = vm["NAME"]
2382
            vm_id   = vm["ID"]
2383

    
2384
            # Get cluster's MOREF and name
2385
            host_id      = vm["HISTORY_RECORDS/HISTORY[last()]/HID"]
2386
            host         = OpenNebula::Host.new_with_id(host_id, one_client)
2387
            rc           = host.info
2388
            raise "\n    ERROR! Could not get host info for wild vm disk. Reason #{rc.message}" if OpenNebula.is_error?(rc)
2389
            ccr_name     = host["NAME"]
2390
            ccr_ref      = host["TEMPLATE/VCENTER_CCR_REF"]
2391
            vcenter_user = host["TEMPLATE/VCENTER_USER"]
2392
            vcenter_pass = host["TEMPLATE/VCENTER_PASSWORD"]
2393
            vcenter_host = host["TEMPLATE/VCENTER_HOST"]
2394
            if !ccr_ref
2395
                raise "VM #{vm_name} could not be updated, cannot find cluster's MOREF"
2396
            end
2397

    
2398
            # Create vi_client
2399
            vi_client    = VCenterDriver::VIClient.new(host_id)
2400
            vcenter_uuid = vi_client.vim.serviceContent.about.instanceUuid
2401
            vcenter_name = vi_client.host
2402

    
2403
            # Is VM a wild?
2404
            vm_wild = vm["TEMPLATE/IMPORTED"] == "YES"
2405

    
2406
            # Try to find the vCenter object comparing its uuid with the DEPLOY_ID
2407
            vc_vmachine        = nil
2408
            vm_ref             = nil
2409
            vc_vmachine_object = nil
2410

    
2411
            machines_found = vc_vmachines[vcenter_uuid].select do |ref, value|
2412
                value["config.uuid"] == vm["DEPLOY_ID"]
2413
            end
2414

    
2415
            if machines_found.size == 0
2416
                STDOUT.puts "VM \e[96m#{vm_name}\e[39m could not be migrated, \e[91mcannot find this VM in objects retrieved\e[39m,\n"\
2417
                            "maybe it was deleted in vCenter but not in OpenNebula?"
2418
                STDOUT.puts
2419
                STDOUT.puts "-" * 80
2420
                STDOUT.puts
2421
                next
2422
            end
2423

    
2424
            if machines_found.size > 1
2425
                # We have several vCenter objects with the same UUID the admin
2426
                # must help us to know which VM is the one we're looking for
2427
                vm_refs   = []
2428
                vm_values = []
2429
                index     = 0
2430
                STDOUT.puts("\n\e[93mWARNING: Manual intervention required!\e[39m")
2431
                STDOUT.puts("\nWhich vCenter VM is represented by OpenNebula \e[96mVM #{vm_name}?\e[39m\n")
2432
                STDOUT.puts
2433

    
2434
                vc_vmachines[vcenter_uuid].each do |ref, v|
2435
                    if v["config.uuid"] == vm["DEPLOY_ID"]
2436
                        item = RbVmomi::VIM::VirtualMachine.new(vi_client.vim, ref)
2437
                        while !item.instance_of? RbVmomi::VIM::Datacenter
2438
                            item = item.parent
2439
                            if item.nil?
2440
                                raise "Could not find the Datacenter associated to this VM"
2441
                            end
2442
                        end
2443
                        datacenter = item
2444

    
2445
                        vm_refs   << ref
2446
                        vm_values << v
2447
                        STDOUT.puts("#{index+1}: VM #{v["name"]} in #{datacenter.name}")
2448
                        index += 1
2449
                    end
2450
                end
2451

    
2452
                loop do
2453
                    STDOUT.print("\nFrom the list above, please \e[95mpick up one number\e[39m in order to specify the right VM: ")
2454
                    vm_index = STDIN.gets.strip.to_i
2455
                    next if vm_index == 0 || vm_index - 1 < 0 || vm_index > vm_refs.size
2456
                    vm_ref  = vm_refs[vm_index-1] rescue nil
2457
                    vc_vmachine = vm_values[vm_index-1] rescue nil
2458
                    vc_vmachine_object = RbVmomi::VIM::VirtualMachine.new(vi_client.vim, vm_ref)
2459
                    break if vm_ref
2460
                end
2461

    
2462
                STDOUT.puts
2463
                STDOUT.puts "-" * 80
2464
                STDOUT.puts
2465

    
2466
            else
2467
                # We have only found one VM where the DEPLOY_ID matches the config.uuid
2468
                vc_vmachines[vcenter_uuid].each do |ref, value|
2469
                    if value["config.uuid"] == vm["DEPLOY_ID"]
2470
                        vc_vmachine = value
2471
                        vc_vmachine_object = RbVmomi::VIM::VirtualMachine.new(vi_client.vim, ref)
2472
                        vm_ref = ref
2473
                        break
2474
                    end
2475
                end
2476
            end
2477

    
2478
            # We have to discriminate between Wild vms and VMs deployed by OpenNebula
2479
            if vm_wild
2480
                template_ref = vm_ref # The template ref is the VM's moref
2481
            else
2482
                # If the VM was deployed by OpenNebula the ref or uuid are in the USER_TEMPLATE
2483
                template_ref  = vm["USER_TEMPLATE/PUBLIC_CLOUD/VCENTER_REF"]
2484
                template_uuid = vm["USER_TEMPLATE/PUBLIC_CLOUD/VM_TEMPLATE"]
2485

    
2486
                # We try to check if the template's moref found inside the XML template
2487
                # is found in the templates retrieved from vcenter only once
2488
                if template_ref
2489

    
2490
                    templates_found = vc_templates[vcenter_uuid].select do |ref, value|
2491
                        template_ref == ref
2492
                    end
2493

    
2494
                    if templates_found.size != 1
2495
                        template_ref = nil
2496
                    end
2497
                end
2498

    
2499
                # Try to get moref using the templates uuid note that that uuid
2500
                # is not unique
2501
                if !template_ref && template_uuid
2502
                    templates_found = 0
2503
                    vc_templates[vcenter_uuid].each do |ref, value|
2504
                        if value["config.uuid"] == template_uuid
2505
                            templates_found += 1
2506
                            if templates_found > 1
2507
                                template_ref = nil
2508
                            else
2509
                                template_ref = ref
2510
                            end
2511
                        end
2512
                    end
2513
                end
2514

    
2515
                if !template_ref
2516
                    STDOUT.puts("\n\e[93mWARNING: Manual intervention required!\e[39m")
2517
                    STDOUT.puts("\nWhich vCenter template is associated with OpenNebula \e[96mVM #{vm_name}?\e[39m\n")
2518
                    STDOUT.puts
2519

    
2520
                    index = 0
2521
                    template_refs  = []
2522

    
2523
                    vc_templates[vcenter_uuid].each do |ref, t|
2524
                        item = RbVmomi::VIM::VirtualMachine.new(vi_client.vim, ref)
2525

    
2526
                        folders = []
2527
                        while !item.instance_of? RbVmomi::VIM::Datacenter
2528
                            item = item.parent
2529
                            if !item.instance_of?(RbVmomi::VIM::Datacenter)
2530
                                if item.name != "vm"
2531
                                    folders << item.name
2532
                                else
2533
                                    folders << ""
2534
                                end
2535
                            end
2536
                            if item.nil?
2537
                                raise "Could not find the Datacenter for the template"
2538
                            end
2539
                        end
2540
                        datacenter = item
2541
                        location   = folders.reverse.join("/")
2542
                        location = "/" if location.empty?
2543

    
2544

    
2545
                        template_refs << ref
2546
                        STDOUT.puts("#{index+1}: Template #{t["name"]} in Datacenter #{datacenter.name} Location: #{location}")
2547
                        index += 1
2548
                    end
2549

    
2550
                    STDOUT.puts("#{template_refs.size+1}: None of the above.")
2551

    
2552
                    loop do
2553
                        STDOUT.print("\nFrom the list above, please \e[95mpick up one number\e[39m in order to specify the venter template that this VM was based on: ")
2554
                        template_index = STDIN.gets.strip.to_i
2555
                        next if template_index == 0 || template_index - 1 < 0 || template_index > template_refs.size + 1
2556
                        template_ref  = template_refs[template_index-1] rescue nil
2557
                        break
2558
                    end
2559
                end
2560

    
2561
                STDOUT.puts
2562
                STDOUT.puts "-" * 80
2563
                STDOUT.puts
2564
            end
2565

    
2566
            # Get VM's datacenter name
2567
            dc = get_dc(vc_vmachine_object)
2568
            dc_name = dc.name
2569
            dc_ref  = dc._ref
2570

    
2571
            # Get xml template from tmp with unmanaged disks and nics and new attributes
2572
            template_id  = vm["TEMPLATE/TEMPLATE_ID"]
2573
            template_xml = nil
2574

    
2575
            if !vm_wild
2576
                template_filename = "#{TEMP_DIR}/one_migrate_template_#{template_id}"
2577
                if File.exist?("#{template_filename}")
2578
                    template_xml = File.open(template_filename) { |f| Nokogiri::XML(f, nil, "UTF-8"){|c| c.default_xml.noblanks} }
2579
                end
2580
            end
2581

    
2582
            # Create a Nokogiri XML representing the VM's template
2583
            xml_doc = Nokogiri::XML(vm.to_xml, nil, "UTF-8"){|c| c.default_xml.noblanks}
2584

    
2585
            # Replace VM's deploy_id uuid with moref
2586
            xml_deploy_id  = xml_doc.root.at_xpath("DEPLOY_ID")
2587
            xml_deploy_id.content = vc_vmachine_object._ref
2588

    
2589
            # Inform about the changes
2590
            STDOUT.puts "VM \e[96m#{vm_name}\e[39m:"
2591
            STDOUT.puts
2592
            STDOUT.puts "--- DEPLOY_ID has been changed to #{vc_vmachine_object._ref}"
2593

    
2594
            # Retrieve and remove disks and nics from vm
2595
            existing_disks = xml_doc.root.xpath("TEMPLATE/DISK").remove # Retrieve and remove existing disks
2596
            existing_nics  = xml_doc.root.xpath("TEMPLATE/NIC").remove  # Retrieve and remove existing nics
2597

    
2598
            # Discover existing disks and/or nics
2599
            # It will return an extraconfig for VM reconfigure
2600
            extraconfig = vm_unmanaged_discover(vc_vmachine["config.hardware.device"],
2601
                                                xml_doc,
2602
                                                template_xml,
2603
                                                existing_disks,
2604
                                                existing_nics,
2605
                                                ccr_name,
2606
                                                ccr_ref,
2607
                                                vcenter_name,
2608
                                                vcenter_uuid,
2609
                                                vcenter_user,
2610
                                                vcenter_pass,
2611
                                                vcenter_host,
2612
                                                dc_name,
2613
                                                dc_ref,
2614
                                                ipool,
2615
                                                vnpool,
2616
                                                dspool,
2617
                                                hpool,
2618
                                                one_client,
2619
                                                vi_client,
2620
                                                vm_wild,
2621
                                                vm_id,
2622
                                                vc_vmachine["name"],
2623
                                                vc_templates[vcenter_uuid],
2624
                                                vm_ref,
2625
                                                vc_vmachines[vcenter_uuid],
2626
                                                template_ref,
2627
                                                one_clusters,
2628
                                                vcenter_ids)
2629

    
2630
            # If VM has TOKEN=YES for CONTEXT we must generate a token.txt file from
2631
            # the variable openebula.token or try to extract it from context
2632

    
2633
            if vm["TEMPLATE/CONTEXT/TOKEN"] == "YES"
2634
                STDOUT.puts
2635
                STDOUT.puts "VM #{vm_name} generating token.txt..."
2636
                onegate_token = vc_vmachine["config.extraConfig"].select{|val| val[:key]=="opennebula.token"}.first.value rescue nil
2637

    
2638
                # For older versions try to extract if from context inside VM
2639
                if !onegate_token
2640
                    context = vc_vmachine["config.extraConfig"].select{|val| val[:key]=="guestinfo.opennebula.context"}.first.value rescue nil
2641
                    if context
2642
                        onegate_token = Base64.decode64(context).split("\n").select{|line| line.start_with?("ONEGATE_TOKEN")}.first[/ONEGATE_TOKEN='(.*?)'/,1] rescue nil
2643
                    end
2644
                end
2645
                STDOUT.put "--- Could not extract token from vcenter vm or context section" if !onegate_token
2646
                File.open("/var/lib/one/vms/#{vm_id}/token.txt",'w'){|f| f.puts(onegate_token)}
2647
            end
2648

    
2649
            # Add opennebula.disk elements to vcenter VM so unmanaged disks are referenced
2650
            spec = {}
2651
            spec[:extraConfig]  = extraconfig if !extraconfig.empty?
2652
            vc_vmachine_object.ReconfigVM_Task(:spec => spec).wait_for_completion
2653

    
2654
            STDOUT.puts
2655
            STDOUT.puts "-" * 80
2656
            STDOUT.puts
2657
        rescue Exception => e
2658
            raise e
2659
        ensure
2660
            vi_client.vim.close if vi_client
2661
        end
2662
    end
2663
end
2664

    
2665
################################################################################
2666
# Pre-migrator tool                                                            #
2667
################################################################################
2668

    
2669
CommandParser::CmdParser.new(ARGV) do
2670
    usage "`vcenter_one54_pre` [<options>]"
2671
    description ""
2672
    version OpenNebulaHelper::ONE_VERSION
2673

    
2674
    helper=OpenNebulaHelper::OneHelper.new
2675

    
2676
    before_proc do
2677
        helper.set_client(options)
2678
    end
2679

    
2680
    cmd_options=CommandParser::OPTIONS-[CommandParser::VERBOSE]
2681
    set :option, cmd_options+OpenNebulaHelper::CLIENT_OPTIONS
2682

    
2683
    option CommandParser::OPTIONS
2684

    
2685
    main do
2686
        begin
2687
            msg = "  vCenter pre-migrator tool for OpenNebula 5.4 - Version: 1.0"
2688
            logo_banner(msg)
2689

    
2690
            # Initialize opennebula client
2691
            one_client = OpenNebula::Client.new()
2692
            vcenter_instances = []
2693

    
2694
            hpool = OpenNebula::HostPool.new(one_client)
2695
            rc = hpool.info
2696

    
2697
            raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
2698

    
2699
            cpool = OpenNebula::ClusterPool.new(one_client)
2700
            rc = cpool.info
2701
            raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
2702

    
2703
            vc_clusters   = {}
2704
            vc_datastores = {}
2705
            vc_networks   = {}
2706
            vc_vmachines  = {}
2707
            vc_templates  = {}
2708

    
2709
            banner " PHASE 0 - Before running the script please read the following notes", true
2710

    
2711
            STDOUT.puts
2712
            STDOUT.puts("- Please check that you have set PERSISTENT_ONLY=\"NO\" and REQUIRED_ATTRS=\"\"\n"\
2713
                        "  in you DS_MAD_CONF vcenter inside the /etc/one/oned.conf and that you have\n"\
2714
                        "  restarted your OpenNebula services to apply the new configuration before\n"\
2715
                        "  launching the script.")
2716
            STDOUT.puts
2717
            STDOUT.puts("- Edit the file \e[92m/var/lib/one/remotes/datastore/vcenter/rm\e[39m and replace the\n"\
2718
                        "  following lines:\n\n"\
2719
                        "  \e[96mvi_client.delete_virtual_disk(img_src,\n"\
2720
                        "                                ds_name)\e[39m \n\n"\
2721
                        "  with the following lines:\n\n"\
2722
                        "  \e[96mif drv_action[\"/DS_DRIVER_ACTION_DATA/IMAGE/TEMPLATE/VCENTER_IMPORTED\"] != \"YES\"\n"\
2723
                        "       vi_client.delete_virtual_disk(img_src,ds_name) \n"\
2724
                        "  end\e[39m \n\n"\
2725
                        "  in order to avoid that you accidentally remove a virtual hard disk from a template \n"\
2726
                        "  or wild VM when you delete an image.")
2727
            STDOUT.puts
2728
            STDOUT.puts("- Note that this script may take some time to perform complex tasks so \e[96mplease be patient.\e[39m")
2729
            STDOUT.puts
2730
            STDOUT.puts("- Although this scripts will do its best to be fully automated there may be situations\n"\
2731
                        "  where a manual intervention is needed, in that case a \e[93mWARNING\e[39m will be shown.")
2732
            STDOUT.puts
2733
            STDOUT.puts("- The virtual networks that represent port groups found inside existing templates\n"\
2734
                        "  will have an Ethernet address range with 255 MACs in the pool. You may want to\n"\
2735
                        "  change or increase this address range after the pre-migrator tool finishes.")
2736
            STDOUT.puts
2737
            STDOUT.puts("- It's advisable to disable the Sunstone user interface before launching this script\n"\
2738
                        "  in order to avoid that OpenNebula objects created by users while\n"\
2739
                        "  the script is running are not pre-migrated by the tool.")
2740
            STDOUT.puts
2741
            STDOUT.puts("- This script can be executed as many times as you wish. It will update previous\n"\
2742
                        "  results and XML template will be always overwritten.")
2743
            STDOUT.puts
2744
            STDOUT.puts("\e[93mDon't forget to restart OpenNebula if you have made changes!\e[39m")
2745

    
2746
            STDOUT.print("\nDo you want to continue? ([y]/n): ")
2747

    
2748
            exit! if STDIN.gets.strip.downcase == 'n'
2749

    
2750
            banner " PHASE 1 - Retrieve objects from vCenter instances", true
2751

    
2752
            STDOUT.puts
2753
            STDOUT.puts "Inventory objects are being retrieved, \e[96mplease be patient...\e[39m"
2754
            STDOUT.puts
2755

    
2756
            # For each vCenter host we:
2757
            # - Create a rbvmomi connection
2758
            # - Generate views for clusters, datastores, networks and VMs
2759

    
2760
            hpool.each do |host|
2761
                next if host['VM_MAD'] != "vcenter"
2762

    
2763
                vi_client = VCenterDriver::VIClient.new(host["ID"])
2764
                vcenter_uuid = vi_client.vim.serviceContent.about.instanceUuid
2765
                if vcenter_instances.include?(vcenter_uuid)
2766
                    vi_client.vim.close
2767
                    next
2768
                end
2769
                vcenter_instances << vcenter_uuid
2770

    
2771
                # Retrieve vCenter Managed Objects
2772
                vc_clusters[vcenter_uuid]   = retrieve_vcenter_clusters(vi_client)
2773
                vc_datastores[vcenter_uuid] = retrieve_vcenter_datastores(vi_client)
2774
                vc_networks[vcenter_uuid]   = retrieve_vcenter_networks(vi_client)
2775
                vc_vmachines[vcenter_uuid], vc_templates[vcenter_uuid]  = retrieve_vcenter_vms(vi_client)
2776

    
2777
                STDOUT.puts "--- #{host["TEMPLATE/VCENTER_HOST"]} \e[92mobjects have been retrieved\e[39m"
2778
            end
2779

    
2780
            STDOUT.puts
2781
            STDOUT.puts "All vCenter objects have been retrieved, thanks for your patience."
2782

    
2783
            # Control what objects id have been modified
2784
            vcenter_ids = {}
2785
            vcenter_ids[:host]  = []
2786
            vcenter_ids[:ds]    = []
2787
            vcenter_ids[:vnet]  = []
2788
            vcenter_ids[:image] = []
2789

    
2790
            banner " PHASE 2 - Add new attributes to existing hosts", true
2791
            add_new_host_attrs(vc_clusters, hpool, one_client, vcenter_ids)
2792

    
2793
            banner " PHASE 3 - Create OpenNebula clusters if needed", true
2794
            STDOUT.puts
2795

    
2796
            one_clusters = create_new_clusters(vc_clusters, hpool, cpool, one_client)
2797

    
2798
            dspool = OpenNebula::DatastorePool.new(one_client)
2799
            rc = dspool.info
2800
            raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
2801

    
2802
            rc = hpool.info # Update host pool to get new attributes
2803
            raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
2804

    
2805
            rc = cpool.info # Update cluster pool to get new clusters
2806
            raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
2807

    
2808
            extended_message = " - Add new attributes to datastores\n"\
2809
                               " - Create SYSTEM datastores if needed\n"\
2810
                               " - Assign datastores to OpenNebula Clusters"
2811

    
2812
            banner " PHASE 4 - Inspect existing datatores ", true, extended_message
2813

    
2814
            inspect_datastores(vc_datastores, vc_clusters, one_clusters, dspool, hpool, one_client, vcenter_ids)
2815

    
2816
            rc = dspool.info # Refresh datastore pool
2817
            raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
2818

    
2819
            vnpool = OpenNebula::VirtualNetworkPool.new(one_client)
2820
            rc = vnpool.info_all
2821
            raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
2822

    
2823
            extended_message = " - Add new attributes to vnets\n"\
2824
                               " - Assign vnets to OpenNebula Clusters"
2825
            banner " PHASE 5 - Add new attributes to existing vnets", true, extended_message
2826
            inspect_networks(vc_networks, vc_clusters, one_clusters, vnpool, hpool, one_client, vcenter_ids)
2827

    
2828
            ipool = OpenNebula::ImagePool.new(one_client)
2829
            rc = ipool.info_all
2830
            raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
2831

    
2832
            banner " PHASE 6 - Add new attributes to existing images", true
2833
            add_new_image_attrs(ipool, one_client, vcenter_ids)
2834

    
2835
            rc = ipool.info_all
2836
            raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
2837

    
2838
            tpool = OpenNebula::TemplatePool.new(one_client)
2839
            rc = tpool.info_all
2840
            raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
2841

    
2842
            extended_message = " - Add new attributes to existing templates\n"\
2843
                               " - Discover nics and disks inside templates\n"\
2844
                               " - Import datastores where discovered virtual disks are found if needed.\n"\
2845
                               " - Create images for discovered virtual hard disks\n"\
2846
                               " - Create vnets for discovered port groups\n"\
2847
                               " - Prepare XML VM templates removing old or deprecated attributes\n"
2848

    
2849
            banner " PHASE 7 - Inspect existing VM templates", true, extended_message
2850

    
2851
            inspect_templates(vc_templates, vc_clusters, one_clusters, tpool, ipool, vnpool, dspool, hpool, one_client, vcenter_ids)
2852

    
2853
            vmpool = OpenNebula::VirtualMachinePool.new(one_client)
2854
            rc = vmpool.info_all
2855
            raise "Error contacting OpenNebula #{rc.message}" if OpenNebula.is_error?(rc)
2856

    
2857
            extended_message = " - Add new attributes to existing VMs\n"\
2858
                               " - Discover nics and disks inside VMs\n"\
2859
                               " - Import datastores where discovered virtual disks are found if needed.\n"\
2860
                               " - Create images for discovered virtual hard disks\n"\
2861
                               " - Create vnets for discovered port groups\n"\
2862
                               " - Prepare XML VM templates removing old or deprecated attributes\n"\
2863
                               " - Reconfigure vCenter VM to add unmanaged disks and nics references\n"\
2864
                               " - DEPLOY_ID will get VM's managed object reference\n"
2865

    
2866
            banner " PHASE 8 - Inspect existing VMs", true
2867
            STDOUT.puts
2868
            inspect_vms(vc_vmachines, vc_templates, vc_clusters, one_clusters, vmpool, ipool, tpool, vnpool, dspool, hpool, one_client, vcenter_ids)
2869

    
2870
            if !vcenter_ids[:host].empty?
2871
                banner " PHASE  9 - Prepare XML templates for hosts without deprecated attributes", true
2872
                prepare_host_xml_templates(vcenter_ids[:host], one_clusters, one_client)
2873
            end
2874

    
2875
            if !vcenter_ids[:ds].empty?
2876
                banner " PHASE 10 - Prepare XML templates for datastores without deprecated attributes", true
2877
                prepare_ds_xml_templates(vcenter_ids[:ds], one_client)
2878
            end
2879

    
2880
            if !vcenter_ids[:vnet].empty?
2881
                banner " PHASE 11 - Prepare XML templates for vnets without deprecated attributes", true
2882
                prepare_vnet_xml_templates(vcenter_ids[:vnet], one_client)
2883
            end
2884

    
2885
            if !vcenter_ids[:image].empty?
2886
                banner " PHASE 12 - Prepare XML templates for images without deprecated attributes", true
2887
                prepare_image_xml_templates(vcenter_ids[:image], hpool, one_client)
2888
            end
2889

    
2890
            puts ""
2891

    
2892
            exit_code 0
2893
        rescue Exception => e
2894
            STDERR.puts "An error occurred when pre-migrating OpenNebula:\n"\
2895
                        "#{e.message}\"\n#{e.backtrace}"
2896
            exit -1
2897
        end
2898
    end
2899
end