poll_xen_kvm.rb

Javi Fontan, 10/05/2015 10:32 AM

Download (25.6 KB)

 
1
#!/usr/bin/env ruby
2

    
3
# -------------------------------------------------------------------------- #
4
# Copyright 2002-2015, 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
require 'pp'
20
require 'rexml/document'
21
require 'base64'
22
require 'uri'
23

    
24
begin
25
    require 'rubygems'
26
    require 'json'
27

    
28
    JSON_LOADED = true
29
rescue LoadError
30
    JSON_LOADED = false
31
end
32

    
33
ENV['LANG']='C'
34
ENV['LC_ALL']='C'
35

    
36
################################################################################
37
#
38
#  KVM Monitor Module
39
#
40
################################################################################
41
module KVM
42
    # Constants for KVM operations
43
    CONF={
44
        :dominfo    => 'virsh --connect LIBVIRT_URI --readonly dominfo',
45
        :list       => 'virsh --connect LIBVIRT_URI --readonly list',
46
        :dumpxml    => 'virsh --connect LIBVIRT_URI --readonly dumpxml',
47
        :domifstat  => 'virsh --connect LIBVIRT_URI --readonly domifstat',
48
        :top        => 'top -b -d2 -n 2 -p ',
49
        'LIBVIRT_URI' => 'qemu:///system'
50
    }
51

    
52
    # Execute a virsh command using the predefined command strings and URI
53
    # @param command [Symbol] as defined in the module CONF constant
54
    def self.virsh(command)
55
        CONF[command].gsub('LIBVIRT_URI', CONF['LIBVIRT_URI'])
56
    end
57

    
58
    # Get the information of a single VM. In case of error the VM is reported
59
    # as not found.
60
    # @param vm_id [String] with the VM information
61
    def self.get_vm_info(one_vm)
62
        dominfo = dom_info(one_vm)
63

    
64
        return { :state => '-' } if !dominfo
65

    
66
        psinfo = process_info(dominfo['UUID'])
67

    
68
        vm = Hash.new
69

    
70
        vm[:name] = one_vm
71
        vm[:pid]  = psinfo[1]
72

    
73
        cpu = get_cpu_info({one_vm => vm})
74

    
75
        resident_mem = psinfo[5].to_i
76
        max_mem      = dominfo['Max memory'].split(/\s+/).first.to_i
77

    
78
        values=Hash.new
79

    
80
        values[:state]  = get_state(dominfo['State'])
81
        values[:cpu]    = cpu[vm[:pid]] if cpu[vm[:pid]]
82
        values[:memory] = [resident_mem, max_mem].max
83

    
84
        xml = dump_xml(one_vm)
85

    
86
        values.merge!(get_interface_statistics(one_vm, xml))
87

    
88
        return values
89
    end
90

    
91
    # Gets the information of all VMs
92
    #
93
    # @return [Hash, nil] Hash with the VM information or nil in case of error
94
    def self.get_all_vm_info
95
        vms_info = Hash.new
96
        vms      = Hash.new
97

    
98
        text=`#{virsh(:list)}`
99

    
100
        return nil if $?.exitstatus != 0
101

    
102
        lines = text.split(/\n/)[2..-1]
103

    
104
        names = lines.map do |line|
105
            line.split(/\s+/).delete_if {|d| d.empty? }[1]
106
        end
107

    
108
        return vms_info if names.length == 0
109

    
110
        names.each do |vm|
111
            dominfo = dom_info(vm)
112

    
113
            if dominfo
114
                psinfo = process_info(dominfo['UUID'])
115

    
116
                info= Hash.new
117

    
118
                info[:dominfo] = dominfo
119
                info[:psinfo]  = psinfo
120
                info[:name]    = vm
121
                info[:pid]     = psinfo[1]
122

    
123
                vms[vm]=info
124
            end
125
        end
126

    
127
        cpu = get_cpu_info(vms)
128

    
129
        vms.each do |name, vm|
130
            ps_data = vm[:psinfo]
131
            dominfo = vm[:dominfo]
132

    
133
            resident_mem = ps_data[5].to_i
134
            max_mem      = dominfo['Max memory'].split(/\s+/).first.to_i
135

    
136
            values = Hash.new
137

    
138
            values[:state]  = get_state(dominfo['State'])
139
            values[:cpu]    = cpu[vm[:pid]] if cpu[vm[:pid]]
140
            values[:memory] = [resident_mem, max_mem].max
141

    
142
            xml = dump_xml(name)
143

    
144
            values.merge!(get_interface_statistics(name, xml))
145
            values.merge!(get_disk_usage(xml))
146

    
147
            if !name.match(/^one-\d+/)
148
                uuid, template = xml_to_one(xml)
149
                values[:template] = Base64.encode64(template).delete("\n")
150
                values[:vm_name] = name
151
                vm[:name] = uuid
152
            end
153

    
154
            vms_info[vm[:name]] = values
155
        end
156

    
157
        return vms_info
158
    end
159

    
160
    # Gathers process information from a set of VMs.
161
    #   @param vms [Hash] of vms indexed by name. Value is a hash with :pid
162
    #   @return  [Hash] with ps information
163
    def self.get_cpu_info(vms)
164
        pids = vms.map {|name, vm| vm[:pid] }
165
        pids.compact!
166

    
167
        cpu = Hash.new
168

    
169
        pids.each_slice(20) do |slice|
170
            data = %x{#{CONF[:top]} #{slice.join(',')}}
171

    
172
            lines = data.strip.split("\n")
173

    
174
            block_size  = lines.length/2
175
            valid_lines = lines.last(block_size)
176

    
177
            first_domain = 7
178

    
179
            cpu_field = nil
180
            valid_lines.each_with_index{ |l,i|
181
                if l.match 'PID USER'
182
                    first_domain=i+1
183
                    cpu_field = l.strip.split.index("%CPU")
184
                    break
185
                end
186
            }
187

    
188
            domain_lines = valid_lines[first_domain..-1]
189

    
190
            domain_lines.each do |line|
191
                d = line.split
192
                cpu[d[0]] = d[cpu_field]
193
            end
194
        end
195

    
196
        cpu
197
    end
198

    
199
    # Process information for a KVM domain by its UUID
200
    #   @param uid [String] with user id
201
    #   @return [Array] of user processes
202
    def self.process_info(uuid)
203
        ps=`ps auxwww | grep -- '-uuid #{uuid}' | grep -v grep`
204
        ps.split(/\s+/)
205
    end
206

    
207
    # Gets the info of a domain by its id
208
    #   @param the ID of the VM as defined in libvirt
209
    #   @return [Hash] with the output of virsh dominfo, indexed by name (Id...)
210
    # Example execution of dominfo
211
    #   Id:             5
212
    #   Name:           one-6
213
    #   UUID:           06bc1876-fc6a-4dca-b41d-d7f2093b6b59
214
    #   OS Type:        hvm
215
    #   State:          running
216
    #   CPU(s):         1
217
    #   CPU time:       11.1s
218
    #   Max memory:     524288 KiB
219
    #   Used memory:    524288 KiB
220
    #   Persistent:     no
221
    #   Autostart:      disable
222
    #   Managed save:   no
223
    #   Security model: none
224
    #   Security DOI:   0
225
    def self.dom_info(vmid)
226
        text = `#{virsh(:dominfo)} #{vmid}`
227

    
228
        return nil if $?.exitstatus != 0
229

    
230
        lines = text.split(/\n/)
231
        hash  = Hash.new
232

    
233
        lines.map do |line|
234
            parts = line.split(/:\s+/)
235

    
236
            hash[parts[0]] = parts[1]
237
        end
238

    
239
        hash
240
    end
241

    
242
    # Get dumpxml output of a VM
243
    #   @param the ID of the VM as defined in libvirt
244
    #   @return [String] xml output of virsh dumpxml
245
    def self.dump_xml(vmid)
246
        `#{virsh(:dumpxml)} '#{vmid}'`
247
    end
248

    
249
    # Aggregate statics of all VM NICs
250
    #   @param the ID of the VM as defined in libvirt
251
    #   @param text [nil, String] dumpxml output or nil to execute dumpxml
252
    #   @return [Hash] with network stats, by name [symbol] :netrx, :nettx
253
    def self.get_interface_statistics(vmid, text = nil)
254
        text = dump_xml(vmid) if !text
255

    
256
        return {} if $?.exitstatus != 0
257

    
258
        doc = REXML::Document.new(text)
259

    
260
        interfaces = Array.new
261

    
262
        doc.elements.each('domain/devices/interface/target') do |ele|
263
            interfaces << ele.attributes["dev"]
264
        end
265

    
266
        return {} if interfaces.empty?
267

    
268
        values = Hash.new
269

    
270
        values[:netrx] = 0
271
        values[:nettx] = 0
272

    
273
        interfaces.each do |interface|
274
            text=`#{virsh(:domifstat)} #{vmid} #{interface}`
275

    
276
            next if $?.exitstatus != 0
277

    
278
            text.each_line do |line|
279
                columns = line.split(/\s+/)
280

    
281
                case columns[1]
282
                    when 'rx_bytes'
283
                        values[:netrx] += columns[2].to_i
284
                    when 'tx_bytes'
285
                        values[:nettx]+=columns[2].to_i
286
                    end
287
                end
288
        end
289

    
290
        values
291
    end
292

    
293
    # Translate libvirt state to Opennebula monitor state
294
    #  @param state [String] libvirt state
295
    #  @return [String] OpenNebula state
296
    #
297
    # Libvirt states for the guest are
298
    #  * 'running' state refers to guests which are currently active on a CPU.
299
    #  * 'blocked' not running or runnable (waiting on I/O or in a sleep mode).
300
    #  * 'paused' after virsh suspend.
301
    #  * 'shutdown' guest in the process of shutting down.
302
    #  * 'dying' the domain has not completely shutdown or crashed.
303
    #  * 'crashed' guests have failed while running and are no longer running.
304
    #
305
    def self.get_state(state)
306
        case state.gsub('-', '')
307
            when *%w{running blocked shutdown dying idle paused}
308
                'a'
309
            when 'crashed'
310
                'e'
311
            else
312
                '-'
313
        end
314
    end
315

    
316
    def self.get_disk_usage(xml)
317
        return {} if !JSON_LOADED
318

    
319
        doc  = REXML::Document.new(xml)
320
        size = 0
321
        systemds = doc.elements['domain/metadata/system_datastore'] rescue nil
322
        systemds = systemds.text.gsub(/\/+/, '/') if systemds
323

    
324
        data = {
325
            :disk_size       => [],
326
            :snapshot_size   => []
327
        }
328

    
329
        doc.elements.each('domain/devices/disk/source') do |ele|
330
            # read the disk path (for regular disks)
331
            file = ele.attributes['file'] rescue nil
332

    
333
            # get protocol and name (for ceph)
334
            protocol = ele.attributes['protocol'] rescue nil
335
            name = ele.attributes['name'] rescue nil
336

    
337
            if protocol == "rbd"
338
                # Ceph
339
                auth = ele.parent.elements["auth"].attributes["username"] rescue nil
340
                auth = "--id #{auth}" if !auth.nil?
341

    
342
                pool, image = name.split('/')
343
                disk_id = image.split('-')[-1].to_i
344

    
345
                images_list = rbd_pool(pool, auth)
346
                images_doc  = REXML::Document.new(images_list)
347

    
348
                xpath = "images/image[image='#{image}']/size"
349
                disk_size = images_doc.elements[xpath].text.to_f/1024/1024
350

    
351
                data[:disk_size] << {:id => disk_id, :size => disk_size.round}
352

    
353
                images_doc.elements.each("images/snapshot") do |snap|
354
                    next unless snap.elements["image"].text.start_with?(image)
355

    
356
                    snap_id = snap.elements["snapshot"].text.to_i
357
                    snapshot_size = snap.elements["size"].text.to_f/1024/1024
358

    
359
                    data[:snapshot_size] << { :id => snap_id, :disk_id => disk_id, :size => snapshot_size.round}
360

    
361
                end
362
            elsif file
363
                # Search the disk in system datastore when the source
364
                # is a persistent image with snapshots
365
                source = nil
366
                current_snap_id = nil
367

    
368
                if !file.match(/.*disk\.\d+$/) && systemds
369
                    source = file.gsub(%r{/+}, '/')
370

    
371
                    disks = Dir["#{systemds}/disk.*"]
372

    
373
                    disks.each do |disk|
374
                        next if !File.symlink?(disk)
375
                        link = File.readlink(disk).gsub(%r{/+}, '/')
376

    
377
                        if link == source
378
                            file = disk
379
                            current_snap_id = link.split('/').last
380
                            break
381
                        end
382
                    end
383
                else
384
                    if File.symlink?(file)
385
                        link = File.readlink(file)
386
                        current_snap_id = link.split('/').last
387
                    end
388
                end
389

    
390
                # Regular Disk
391
                text = `qemu-img info --output=json #{file}`
392
                next if !$? || !$?.success?
393

    
394
                json = JSON.parse(text)
395

    
396
                disk_id = file.split(".")[-1]
397

    
398
                disk_size = json['actual-size'].to_f/1024/1024
399

    
400
                data[:disk_size] << {:id => disk_id, :size => disk_size.round}
401

    
402
                # Get snapshots
403
                Dir[file + '.snap/*'].each do |snap|
404
                    if current_snap_id
405
                        next if snap.split('/').last == current_snap_id
406
                    else
407
                        next if source == snap
408
                    end
409

    
410
                    text = `qemu-img info --output=json #{snap}`
411
                    next if !$? || !$?.success?
412

    
413
                    json = JSON.parse(text)
414

    
415
                    snap_id = snap.split("/")[-1]
416

    
417
                    snap_size = json['actual-size'].to_f/1024/1024
418

    
419
                    data[:snapshot_size] << { :id => snap_id, :disk_id => disk_id, :size => snap_size.round}
420
                end
421
            end
422
        end
423

    
424
        data
425
    end
426

    
427
    # Convert the output of dumpxml to an OpenNebula template
428
    #   @param xml [String] output of dumpxml
429
    #   @return [Array] uuid and OpenNebula template encoded in base64
430
    def self.xml_to_one(xml)
431
        doc = REXML::Document.new(xml)
432

    
433
        name = REXML::XPath.first(doc, '/domain/name').text
434
        uuid = REXML::XPath.first(doc, '/domain/uuid').text
435
        vcpu = REXML::XPath.first(doc, '/domain/vcpu').text
436
        memory = REXML::XPath.first(doc, '/domain/memory').text.to_i / 1024
437
        arch = REXML::XPath.first(doc, '/domain/os/type').attributes['arch']
438

    
439
=begin
440
        disks = []
441
        REXML::XPath.each(doc, '/domain/devices/disk') do |d|
442
            type = REXML::XPath.first(d, '//disk').attributes['type']
443
            driver = REXML::XPath.first(d, '//disk/driver').attributes['type']
444
            source = REXML::XPath.first(d, '//disk/source').attributes[type]
445
            target = REXML::XPath.first(d, '//disk/target').attributes['dev']
446

447
            disks << {
448
                :type => type,
449
                :driver => driver,
450
                :source => source,
451
                :target => target
452
            }
453
        end
454

455
        disks_txt = ''
456

457
        disks.each do |disk|
458
            disks_txt << "DISK=[\n"
459
            disks_txt << "  SOURCE=\"#{disk[:source]}\",\n"
460
            disks_txt << "  DRIVER=\"#{disk[:driver]}\",\n"
461
            disks_txt << "  TARGET=\"#{disk[:target]}\""
462
            disks_txt << "]\n"
463
        end
464

465

466
        interfaces = []
467
        REXML::XPath.each(doc,
468
                "/domain/devices/interface[@type='bridge']") do |i|
469
            mac = REXML::XPath.first(i, '//interface/mac').
470
                attributes['address']
471
            bridge = REXML::XPath.first(i, '//interface/source').
472
                attributes['bridge']
473
            model = REXML::XPath.first(i, '//interface/model').
474
                attributes['type']
475

476
            interfaces << {
477
                :mac => mac,
478
                :bridge => bridge,
479
                :model => model
480
            }
481
        end
482

483
        interfaces_txt = ''
484

485
        interfaces.each do |interface|
486
            interfaces_txt << "NIC=[\n"
487
            interfaces_txt << "  MAC=\"#{interface[:mac]}\",\n"
488
            interfaces_txt << "  BRIDGE=\"#{interface[:bridge]}\",\n"
489
            interfaces_txt << "  MODEL=\"#{interface[:model]}\""
490
            interfaces_txt << "]\n"
491
        end
492
=end
493

    
494
        spice = REXML::XPath.first(doc,
495
            "/domain/devices/graphics[@type='spice']")
496
        spice = spice.attributes['port'] if spice
497

    
498
        spice_txt = ''
499
        if spice
500
            spice_txt = %Q<GRAPHICS = [ TYPE="spice", PORT="#{spice}" ]>
501
        end
502

    
503
        vnc = REXML::XPath.first(doc, "/domain/devices/graphics[@type='vnc']")
504
        vnc = vnc.attributes['port'] if vnc
505

    
506
        vnc_txt = ''
507
        if vnc
508
            vnc_txt = %Q<GRAPHICS = [ TYPE="vnc", PORT="#{vnc}" ]>
509
        end
510

    
511

    
512
        feature_list = %w{acpi apic pae}
513
        features = []
514

    
515
        feature_list.each do |feature|
516
            if REXML::XPath.first(doc, "/domain/features/#{feature}")
517
                features << feature
518
            end
519
        end
520

    
521
        feat = []
522
        features.each do |feature|
523
            feat << %Q[  #{feature.upcase}="yes"]
524
        end
525

    
526
        features_txt = "FEATURES=[\n"
527
        features_txt << feat.join(",\n")
528
        features_txt << "]\n"
529

    
530

    
531
        template = <<EOT
532
NAME="#{name}"
533
CPU=#{vcpu}
534
VCPU=#{vcpu}
535
MEMORY=#{memory}
536
HYPERVISOR="kvm"
537
IMPORT_VM_ID="#{uuid}"
538
OS=[ARCH="#{arch}"]
539
#{features_txt}
540
#{spice_txt}
541
#{vnc_txt}
542
EOT
543

    
544
        return uuid, template
545
    end
546

    
547
    def self.rbd_pool(pool, auth = nil)
548
        @@rbd_pool ||= {}
549

    
550
        if @@rbd_pool[pool].nil?
551
            @@rbd_pool[pool] = `rbd #{auth} ls -l -p #{pool} --format xml`
552
        end
553

    
554
        @@rbd_pool[pool]
555
    end
556
end
557

    
558
################################################################################
559
#
560
# Xen Monitor Module
561
#
562
################################################################################
563
module XEN
564
    # Default configuration variables. It can be overridden through xenrc
565
    CONF={
566
        'XM_POLL' => 'sudo /usr/sbin/xentop -bi2'
567
    }
568

    
569
    # Get the information of a single VM. In case of error the VM is reported
570
    # as not found.
571
    # @param vm_id [String] with the VM information
572
    def self.get_vm_info(vm_id)
573
        data = get_all_vm_info
574

    
575
        if !data
576
            return {:STATE => 'd'}
577
        else
578
            return data[vm_id]
579
        end
580
    end
581

    
582
    # Gets the information of all VMs
583
    #
584
    # @return [Hash, nil] Hash with the VM information or nil in case of error
585
    def self.get_all_vm_info
586
        begin
587
            begin
588
                list_long = get_vm_list_long
589
            rescue
590
                list_long = []
591
            end
592

    
593
            vm_templates = get_vm_templates(list_long)
594
            vm_disk_stats = get_vm_disk_stats(list_long)
595

    
596
            text  = `#{CONF['XM_POLL']}`
597

    
598
            return nil if $?.exitstatus != 0
599

    
600
            lines = text.strip.split("\n")
601

    
602
            block_size  = lines.length/2
603
            valid_lines = lines.last(block_size)
604

    
605
            first_domain = 4
606

    
607
            valid_lines.each_with_index{ |l,i|
608
                if l.match 'NAME  STATE'
609
                    first_domain=i+1
610
                    break
611
                end
612
            }
613

    
614
            domain_lines = valid_lines[first_domain..-1]
615

    
616
            domains = Hash.new
617

    
618
            domain_lines.each do |dom|
619
                dom_data = dom.gsub('no limit', 'no-limit').strip.split
620

    
621
                name = dom_data[0]
622

    
623
                dom_hash = Hash.new
624

    
625
                dom_hash[:name]    = name
626
                dom_hash[:vm_name] = name
627
                dom_hash[:state]   = get_state(dom_data[1])
628
                dom_hash[:cpu]     = dom_data[3]
629
                dom_hash[:memory]  = dom_data[4]
630
                dom_hash[:nettx]   = dom_data[10].to_i * 1024
631
                dom_hash[:netrx]   = dom_data[11].to_i * 1024
632

    
633
                if !name.match(/^one-\d/) && vm_templates[name]
634
                    dom_hash[:template] =
635
                        Base64.encode64(vm_templates[name]).delete("\n")
636
                end
637

    
638
                dom_hash.merge!(vm_disk_stats[name]) if vm_disk_stats[name]
639

    
640
                domains[name] = dom_hash
641
            end
642

    
643
            domains
644
        rescue
645
            STDERR.puts "Error executing #{CONF['XM_POLL']}"
646
            nil
647
        end
648
    end
649

    
650
    # Returns an OpenNebula state from the Xen status
651
    # @param state [String] with the Xen status
652
    # @return [String] OpenNebula monitor state
653
    #
654
    # Xentop states are:
655
    #  'd' – domain is dying
656
    #  's' – domain shutting down
657
    #  'b' – blocked domain
658
    #  'c' – domain crashed
659
    #  'p' – domain paused
660
    #  'r' – domain is actively ruining on one of the CPU
661
    def self.get_state(state)
662
        case state.gsub('-', '')[-1..-1]
663
        when *%w{r b s d p}
664
            'a'
665
        when 'c'
666
            'e'
667
        else
668
            '-'
669
        end
670
    end
671

    
672
    def self.get_vm_list_long
673
        return {} if !JSON_LOADED
674

    
675
        text = `#{CONF['XM_LIST']} -l`
676
        doms = JSON.parse(text)
677
    end
678

    
679
    def self.get_vm_templates(doms)
680
        dom_tmpl = {}
681

    
682
        doms.each do |dom|
683
            name = dom['config']['c_info']['name']
684
            name = URI.escape(name)
685

    
686
            tmp = %Q<NAME = "#{name}"\n>
687
            tmp << %Q<IMPORT_VM_ID = "#{name}"\n>
688

    
689
            vcpus = dom['config']['b_info']['max_vcpus'].to_i
690
            vcpus = 1 if vcpus < 1
691

    
692
            tmp << %Q<CPU = #{vcpus}\n>
693
            tmp << %Q<VCPU = #{vcpus}\n>
694

    
695
            memory = dom['config']['b_info']['max_memkb']
696
            memory /= 1024
697

    
698
            tmp << %Q<MEMORY = #{memory}\n>
699

    
700
            dom_tmpl[name] = tmp
701
        end
702

    
703
        dom_tmpl
704
    end
705

    
706
    def self.get_vm_disk_stats(doms)
707
        dom_disk_stats = {}
708

    
709
        doms.each do |dom|
710
            data = {
711
                :disk_size       => [],
712
                :snapshot_size   => []
713
            }
714

    
715
            dom['config']['disks'].each do |disk|
716
                next if !disk['pdev_path']
717

    
718
                path = disk['pdev_path']
719

    
720
                text = `qemu-img info --output=json #{path}`
721
                next if !$? || !$?.success?
722

    
723
                json = JSON.parse(text)
724

    
725
                disk_id = path.split(".")[-1]
726

    
727
                disk_size = json['actual-size'].to_f/1024/1024
728

    
729
                data[:disk_size] << {:id => disk_id, :size => disk_size.round}
730
            end
731

    
732

    
733
            data
734
        end
735

    
736
        dom_disk_stats
737
    end
738
end
739

    
740
################################################################################
741
# Functions to interface hypervisor information
742
################################################################################
743

    
744
# Selects the hypervisor to be used based on the arguments or probe location
745
# This function also loads the associated configuration variables.
746
# @return [Module] with the hypervisor XEN, KVM
747
def setup_hypervisor
748
    hypervisor = nil
749
    params     = ARGV.clone
750

    
751
    params.each_with_index do |param, index|
752
        case param
753
            when '--kvm'
754
                hypervisor = KVM
755
                ARGV.delete_at(index)
756
            when '--xen'
757
                hypervisor = XEN
758
                ARGV.delete_at(index)
759
        end
760
    end
761

    
762
    if !hypervisor
763
        case $0
764
            when %r{/vmm\/kvm/}
765
                hypervisor=KVM
766
            when %r{/vmm\/xen\d?/}
767
                hypervisor=XEN
768
        end
769
    end
770

    
771
    case hypervisor.name
772
        when 'XEN'
773
            file = 'xenrc'
774
            vars = %w{XM_POLL XM_LIST}
775
        when 'KVM'
776
            file = 'kvmrc'
777
            vars = %w{LIBVIRT_URI}
778
        else
779
            return nil
780
    end
781

    
782
    # Load the rc variables and override the default values
783
    begin
784
        env   = `. #{File.dirname($0)+"/#{file}"};env`
785
        lines = env.split("\n")
786

    
787
        vars.each do |var|
788
            lines.each do |line|
789
                if a = line.match(/^(#{var})=(.*)$/)
790
                    hypervisor::CONF[var] = a[2]
791
                    break
792
                end
793
            end
794
        end
795
    rescue
796
    end
797

    
798
    return hypervisor
799
end
800

    
801
# Returns an OpenNebula monitor string
802
# @param name [String] of the monitor metric
803
# @param value [String] of the monitor metric
804
# @return [String, nil]
805
def print_data(name, value)
806
    return nil if value.nil? || (value.respond_to?(:empty?) && value.empty?)
807

    
808
    if value.instance_of? Array
809
        data_str = ""
810
        value.each do |v|
811
            data_str += print_data(name, v)
812
        end
813

    
814
        return data_str
815
    elsif value.instance_of? Hash
816
        values = value.map do |k,v|
817
            "#{k.to_s.upcase}=#{v}"
818
        end.join(", ")
819

    
820
        return "#{name.to_s.upcase}=[ #{values} ] "
821
    else
822
        return "#{name.to_s.upcase}=#{value}"
823
    end
824
end
825

    
826
# Puts to STDOUT a string in the form "VAL1=VAR1 VAL2=VAR2" with the monitor
827
# attributes of the VM
828
# @param hypervisor [Module]
829
# @param vm_id [String] with the VM ID
830
def print_one_vm_info(hypervisor, vm_id)
831
    info = hypervisor.get_vm_info(vm_id)
832

    
833
    exit(-1) if !info
834

    
835
    values = info.map do |key, value|
836
        print_data(key, value)
837
    end
838

    
839
    puts values.zip.join(' ')
840
end
841

    
842
def print_all_vm_info(hypervisor)
843
    require 'yaml'
844
    require 'zlib'
845

    
846
    vms = hypervisor.get_all_vm_info
847

    
848
    return nil if vms.nil?
849

    
850
    compressed = Zlib::Deflate.deflate(vms.to_yaml)
851

    
852
    puts Base64.encode64(compressed).delete("\n")
853
end
854

    
855
def print_all_vm_template(hypervisor)
856
    vms=hypervisor.get_all_vm_info
857

    
858
    return nil if vms.nil?
859

    
860
    puts "VM_POLL=YES"
861

    
862
    vms.each do |name, data|
863
        number = -1
864

    
865
        if (name =~ /^one-\d*$/)
866
            number = name.split('-').last
867
        end
868

    
869
        vm_name = data[:vm_name]
870

    
871
        string  = "VM=[\n"
872
        string << "  ID=#{number},\n"
873
        string << "  DEPLOY_ID=#{name},\n"
874
        string << %Q(  VM_NAME="#{vm_name}",\n) if vm_name
875

    
876
        if data[:template]
877
            string << %Q(  IMPORT_TEMPLATE="#{data[:template]}",\n)
878
            data.delete(:template)
879
        end
880

    
881
        values = data.map do |key, value|
882
            print_data(key, value)
883
        end
884

    
885
        monitor = values.zip.join(' ')
886

    
887
        string << "  POLL=\"#{monitor}\" ]"
888

    
889
        puts string
890
    end
891
end
892

    
893

    
894
################################################################################
895
# MAIN PROGRAM
896
################################################################################
897

    
898
hypervisor = setup_hypervisor
899

    
900
if !hypervisor
901
    STDERR.puts "Could not detect hypervisor"
902
    exit(-1)
903
end
904

    
905
vm_id = ARGV[0]
906

    
907
if vm_id == '-t'
908
    print_all_vm_template(hypervisor)
909
elsif vm_id
910
    print_one_vm_info(hypervisor, vm_id)
911
else
912
    print_all_vm_info(hypervisor)
913
end