one_vmm_exec.rb

NIC functions when vmm exec - Sébastien LEFEUVRE, 05/27/2015 10:43 AM

Download (29.1 KB)

 
1
#!/usr/bin/env ruby
2

    
3
# -------------------------------------------------------------------------- #
4
# Copyright 2002-2015, OpenNebula Project (OpenNebula.org), C12G Labs        #
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
# Set up the environment for the driver
20

    
21
ONE_LOCATION = ENV["ONE_LOCATION"]
22

    
23
if !ONE_LOCATION
24
    RUBY_LIB_LOCATION = "/usr/lib/one/ruby"
25
    MAD_LOCATION      = "/usr/lib/one/mads"
26
    ETC_LOCATION      = "/etc/one/"
27
else
28
    RUBY_LIB_LOCATION = ONE_LOCATION + "/lib/ruby"
29
    MAD_LOCATION      = ONE_LOCATION + "/lib/mads"
30
    ETC_LOCATION      = ONE_LOCATION + "/etc/"
31
end
32

    
33
$: << RUBY_LIB_LOCATION
34
$: << MAD_LOCATION
35

    
36
require "VirtualMachineDriver"
37
require 'one_vnm'
38
require 'one_tm'
39
require 'getoptlong'
40
require 'ssh_stream'
41
require 'rexml/document'
42

    
43
require 'pp'
44

    
45
class VmmAction
46
    # List of xpaths required by the VNM driver actions
47
    XPATH_LIST = %w(ID DEPLOY_ID TEMPLATE/NIC HISTORY_RECORDS/HISTORY/HOSTNAME)
48

    
49
    attr_reader :data
50

    
51
    # Initialize a VmmAction object
52
    # @param[OpenNebula::ExecDriver] Driver to be used for the actions
53
    # @param[String] Id of the VM
54
    # @param[String] name of the actions as described in the VMM protocol
55
    # @param[xml_data] data sent from OpenNebula core
56
    def initialize(driver, id, action, xml_data)
57
        # Initialize object with xml data
58
        @vmm = driver
59
        @id  = id
60

    
61
        @main_action = action
62
        @xml_data    = @vmm.decode(xml_data)
63

    
64
        @data = Hash.new
65

    
66
        get_data(:host)
67
        get_data(:net_drv)
68
        get_data(:deploy_id)
69
        get_data(:checkpoint_file)
70

    
71
        get_data(:local_dfile, :LOCAL_DEPLOYMENT_FILE)
72
        get_data(:remote_dfile, :REMOTE_DEPLOYMENT_FILE)
73

    
74
        # For migration
75
        get_data(:dest_host, :MIGR_HOST)
76
        get_data(:dest_driver, :MIGR_NET_DRV)
77

    
78
        # For disk hotplugging
79
        get_data(:disk_target_path)
80
        get_data(:tm_command)
81

    
82
        # VM template
83
        vm_template = @xml_data.elements['VM'].to_s
84
        @data[:vm]  = Base64.encode64(vm_template).delete("\n")
85

    
86
        # VM data for VNM
87
        vm_template_xml = REXML::Document.new(vm_template).root
88
        vm_vnm_xml = REXML::Document.new('<VM></VM>').root
89

    
90
        XPATH_LIST.each do |xpath|
91
            elements = vm_template_xml.elements.each(xpath) do |element|
92
                add_element_to_path(vm_vnm_xml, element, xpath)
93
            end
94
        end
95

    
96
        # Initialize streams and vnm
97
        @ssh_src = @vmm.get_ssh_stream(action, @data[:host], @id)
98
        @vnm_src = VirtualNetworkDriver.new(@data[:net_drv],
99
                            :local_actions  => @vmm.options[:local_actions],
100
                            :message        => vm_vnm_xml.to_s,
101
                            :ssh_stream     => @ssh_src)
102

    
103
        if @data[:dest_host] and !@data[:dest_host].empty?
104
            @ssh_dst = @vmm.get_ssh_stream(action, @data[:dest_host], @id)
105
            @vnm_dst = VirtualNetworkDriver.new(@data[:dest_driver],
106
                            :local_actions  => @vmm.options[:local_actions],
107
                            :message        => vm_vnm_xml.to_s,
108
                            :ssh_stream     => @ssh_dst)
109
        end
110

    
111
        @tm = TransferManagerDriver.new(nil)
112
    end
113

    
114
    #Execute a set of steps defined with
115
    #  - :driver :vmm or :vnm to execute the step
116
    #  - :action for the step
117
    #  - :parameters command line paremeters for the action
118
    #  - :destination use next host
119
    #  - :fail_action steps to be executed if steps fail
120
    #  - :stdin for the action
121
    #  @param [Array] of steps
122
    def run(steps, info_on_success = nil)
123
        result = execute_steps(steps)
124

    
125
        @ssh_src.close if @ssh_src
126
        @ssh_dst.close if @ssh_dst
127

    
128
        #Prepare the info for the OpenNebula core
129
        if DriverExecHelper.failed?(result)
130
            info = @data[:failed_info]
131
        else
132
            info = @data["#{@main_action.to_s}_info".to_sym]
133
        end
134

    
135
        @vmm.send_message(VirtualMachineDriver::ACTION[@main_action],
136
                          result, @id, info)
137
    end
138

    
139
    private
140

    
141
    DRIVER_NAMES = {
142
        :vmm => "virtualization driver",
143
        :vnm => "network driver",
144
        :tm => "transfer manager driver"
145
    }
146

    
147
    # Executes a set of steps. If one step fails any recover action is performed
148
    # and the step execution breaks.
149
    # @param [Array] array of steps to be executed
150
    # @return [String, Hash] "SUCCESS/FAILURE" for the step set, and
151
    # information associated to each step (by :<action>_info). In case of
152
    # failure information is also in [:failed_info]
153
    def execute_steps(steps)
154
        result = DriverExecHelper.const_get(:RESULT)[:failure]
155

    
156
        steps.each do |step|
157
            # Execute Step
158
            case step[:driver]
159
            when :vmm
160
                if step[:destination]
161
                    host = @data[:dest_host]
162
                    ssh  = @ssh_dst
163
                else
164
                    host = @data[:host]
165
                    ssh  = @ssh_src
166
                end
167

    
168
                result, info = @vmm.do_action(get_parameters(step[:parameters]),
169
                                              @id,
170
                                              host,
171
                                              step[:action],
172
                                              :ssh_stream => ssh,
173
                                              :respond => false,
174
                                              :stdin => step[:stdin])
175
            when :vnm
176
                if step[:destination]
177
                    vnm = @vnm_dst
178
                else
179
                    vnm = @vnm_src
180
                end
181

    
182
                result, info = vnm.do_action(@id, step[:action],
183
                            :parameters => get_parameters(step[:parameters]))
184
            when :tm
185
                result, info = @tm.do_transfer_action(@id, step[:parameters])
186

    
187
            else
188
                result = DriverExecHelper.const_get(:RESULT)[:failure]
189
                info   = "No driver in #{step[:action]}"
190
            end
191

    
192
            # Save the step info
193
            @data["#{step[:action]}_info".to_sym] = info.strip
194

    
195
            # Roll back steps, store failed info and break steps
196
            if DriverExecHelper.failed?(result)
197
                execute_steps(step[:fail_actions]) if step[:fail_actions]
198
                @data[:failed_info] = info
199

    
200
                @vmm.log(@id,
201
                         "Failed to execute #{DRIVER_NAMES[step[:driver]]} " \
202
                         "operation: #{step[:action]}.")
203

    
204
                if step[:no_fail]
205
                    result = DriverExecHelper::RESULT[:success]
206
                else
207
                    break
208
                end
209
            else
210
                @vmm.log(@id,
211
                         "Successfully execute #{DRIVER_NAMES[step[:driver]]} " \
212
                         "operation: #{step[:action]}.")
213
            end
214
        end
215

    
216
        return result
217
    end
218

    
219
    # Prepare the parameters for the action step generating a blank separated
220
    # list of command arguments
221
    # @param [Hash] an action step
222
    def get_parameters(step_params)
223
        parameters = step_params || []
224

    
225
        parameters.map do |param|
226
            if Symbol===param
227
               "\'#{@data[param].to_s}\'"
228
            else
229
               "\'#{param}\'"
230
            end
231
        end.join(' ')
232
    end
233

    
234
    # Extracts data from the XML argument of the VMM action
235
    # @param [Symbol] corresponding to a XML element
236
    # @param [String] an xpath for the XML element
237
    # @return [String] the element value
238
    def get_data(name, xml_path=nil)
239
        if xml_path
240
            path=xml_path.to_s
241
        else
242
            path=name.to_s.upcase
243
        end
244

    
245
        if (elem = @xml_data.elements[path])
246
            @data[name]=elem.text
247
        end
248
    end
249

    
250
    # Adds a REXML node to a specific xpath
251
    #
252
    # @param [REXML::Element] xml document to add to
253
    # @param [REXML::Element] element to add
254
    # @param [String] path where the element is inserted in the xml document
255
    # @return [REXML::Element]
256
    def add_element_to_path(xml, element, path)
257
        root = xml
258
        path.split('/')[0..-2].each do |path_element|
259
            xml = xml.add_element(path_element) if path_element
260
        end
261
        xml.add_element(element)
262
        root
263
    end
264
end
265

    
266

    
267
# The main class for the Sh driver
268
class ExecDriver < VirtualMachineDriver
269
    attr_reader :options
270

    
271
    # Initializes the VMM driver
272
    # @param [String] hypervisor name identifies the plugin
273
    # @param [OpenNebulaDriver::options]
274
    def initialize(hypervisor, options={})
275
        @options={
276
            :threaded => true
277
        }.merge!(options)
278

    
279
        if options[:shell]
280
            @shell=options[:shell]
281
        else
282
            @shell='bash'
283
        end
284

    
285
        super("vmm/#{hypervisor}", @options)
286

    
287
        @hypervisor  = hypervisor
288
    end
289

    
290
    # Creates an SshStream to execute commands on the target host
291
    # @param[String] the hostname of the host
292
    # @param[String] id of the VM to log messages
293
    # @return [SshStreamCommand]
294
    def get_ssh_stream(aname, host, id)
295
        SshStreamCommand.new(host,
296
                            @remote_scripts_base_path,
297
                            log_method(id), nil, @shell)
298
    end
299

    
300
    #---------------------------------------------------------------------------
301
    #  Virtual Machine Manager Protocol Actions
302
    #---------------------------------------------------------------------------
303
    #
304
    # DEPLOY action, sends the deployment file to remote host
305
    #
306
    def deploy(id, drv_message)
307
        action = VmmAction.new(self, id, :deploy, drv_message)
308

    
309
        # ----------------------------------------------------------------------
310
        #  Initialization of deployment data
311
        # ----------------------------------------------------------------------
312
        local_dfile=action.data[:local_dfile]
313

    
314
        if !local_dfile || File.zero?(local_dfile)
315
            send_message(ACTION[:deploy],RESULT[:failure],id,
316
                "Cannot open deployment file #{local_dfile}")
317
            return
318
        end
319

    
320
        domain = File.read(local_dfile)
321

    
322
        if action_is_local?(:deploy)
323
            dfile = action.data[:local_dfile]
324
        else
325
            dfile = action.data[:remote_dfile]
326
        end
327

    
328
        # ----------------------------------------------------------------------
329
        #  Deployment Steps
330
        # ----------------------------------------------------------------------
331

    
332
        steps=[
333
            # Execute pre-boot networking setup
334
            {
335
                :driver   => :vnm,
336
                :action   => :pre
337
            },
338
            # Boot the Virtual Machine
339
            {
340
                :driver       => :vmm,
341
                :action       => :deploy,
342
                :parameters   => [dfile, :host],
343
                :stdin        => domain,
344
            },
345
            # Execute post-boot networking setup
346
            {
347
                :driver       => :vnm,
348
                :action       => :post,
349
                :parameters   => [:deploy_info],
350
                :fail_actions => [
351
                    {
352
                        :driver     => :vmm,
353
                        :action     => :cancel,
354
                        :parameters => [:deploy_info, :host]
355
                    }
356
                ]
357
            }
358
        ]
359

    
360
        action.run(steps)
361
    end
362

    
363
    #
364
    # SHUTDOWN action, graceful shutdown and network clean up
365
    #
366
    def shutdown(id, drv_message)
367

    
368
        action = VmmAction.new(self, id, :shutdown, drv_message)
369

    
370
        steps=[
371
            # Shutdown the Virtual Machine
372
            {
373
                :driver     => :vmm,
374
                :action     => :shutdown,
375
                :parameters => [:deploy_id, :host]
376
            },
377
            # Execute networking clean up operations
378
            {
379
                :driver   => :vnm,
380
                :action   => :clean
381
            }
382
        ]
383

    
384
        action.run(steps)
385
    end
386

    
387
    #
388
    # CANCEL action, destroys a VM and network clean up
389
    #
390
    def cancel(id, drv_message)
391
        action = VmmAction.new(self, id, :cancel, drv_message)
392

    
393
        steps=[
394
            # Cancel the Virtual Machine
395
            {
396
                :driver     => :vmm,
397
                :action     => :cancel,
398
                :parameters => [:deploy_id, :host]
399
            },
400
            # Execute networking clean up operations
401
            {
402
                :driver   => :vnm,
403
                :action   => :clean
404
            }
405
        ]
406

    
407
        action.run(steps)
408
    end
409

    
410
    #
411
    # SAVE action, stops the VM and saves its state, network is cleaned up
412
    #
413
    def save(id, drv_message)
414
        action = VmmAction.new(self, id, :save, drv_message)
415

    
416
        steps=[
417
            # Save the Virtual Machine state
418
            {
419
                :driver     => :vmm,
420
                :action     => :save,
421
                :parameters => [:deploy_id, :checkpoint_file, :host]
422
            },
423
            # Execute networking clean up operations
424
            {
425
                :driver   => :vnm,
426
                :action   => :clean
427
            }
428
        ]
429

    
430
        action.run(steps)
431
    end
432

    
433
    #
434
    # RESTORE action, restore a VM from a previous state, and restores network
435
    #
436
    def restore(id, drv_message)
437
        action=VmmAction.new(self, id, :restore, drv_message)
438

    
439
        steps=[
440
            # Execute pre-boot networking setup
441
            {
442
                :driver     => :vnm,
443
                :action     => :pre
444
            },
445
            # Restore the Virtual Machine from checkpoint
446
            {
447
                :driver     => :vmm,
448
                :action     => :restore,
449
                :parameters => [:checkpoint_file, :host, :deploy_id]
450
            },
451
            # Execute post-boot networking setup
452
            {
453
                :driver       => :vnm,
454
                :action       => :post,
455
                :parameters   => [:deploy_id],
456
                :fail_actions => [
457
                    {
458
                        :driver     => :vmm,
459
                        :action     => :cancel,
460
                        :parameters => [:deploy_id, :host]
461
                    }
462
                ],
463
            }
464
        ]
465

    
466
        action.run(steps)
467
    end
468

    
469
    #
470
    # MIGRATE (live) action, migrates a VM to another host creating network
471
    #
472
    def migrate(id, drv_message)
473
        action = VmmAction.new(self, id, :migrate, drv_message)
474
        pre    = "PRE"
475
        post   = "POST"
476

    
477
        pre  << action.data[:tm_command] << " " << action.data[:vm]
478
        post << action.data[:tm_command] << " " << action.data[:vm]
479

    
480
        steps=[
481
            # Execute a pre-migrate TM setup
482
            {
483
                :driver     => :tm,
484
                :action     => :tm_premigrate,
485
                :parameters => pre.split
486
            },
487
            # Execute pre-boot networking setup on migrating host
488
            {
489
                :driver      => :vnm,
490
                :action      => :pre,
491
                :destination => true
492
            },
493
            # Migrate the Virtual Machine
494
            {
495
                :driver     => :vmm,
496
                :action     => :migrate,
497
                :parameters => [:deploy_id, :dest_host, :host]
498
            },
499
            # Execute networking clean up operations
500
            # NOTE: VM is now in the new host. If we fail from now on, oned will
501
            # assume that the VM is in the previous host but it is in fact
502
            # migrated. Log errors will be shown in vm.log
503
            {
504
                :driver       => :vnm,
505
                :action       => :clean,
506
                :no_fail      => true
507
            },
508
            # Execute post-boot networking setup on migrating host
509
            {
510
                :driver       => :vnm,
511
                :action       => :post,
512
                :parameters   => [:deploy_id],
513
                :destination  => :true,
514
                :no_fail      => true
515
            },
516
            {
517
                :driver     => :tm,
518
                :action     => :tm_postmigrate,
519
                :parameters => post.split,
520
                :no_fail    => true
521
            },
522
        ]
523

    
524
        action.run(steps)
525
    end
526

    
527
    #
528
    # POLL action, gets information of a VM
529
    #
530
    def poll(id, drv_message)
531
        data      = decode(drv_message)
532
        host      = data.elements['HOST'].text
533
        deploy_id = data.elements['DEPLOY_ID'].text
534

    
535
        do_action("#{deploy_id} #{host}", id, host, ACTION[:poll])
536
    end
537

    
538
    #
539
    # REBOOT action, reboots a running VM
540
    #
541
    def reboot(id, drv_message)
542
        data      = decode(drv_message)
543
        host      = data.elements['HOST'].text
544
        deploy_id = data.elements['DEPLOY_ID'].text
545

    
546
        do_action("#{deploy_id} #{host}", id, host, ACTION[:reboot])
547
    end
548

    
549
    #
550
    # RESET action, resets a running VM
551
    #
552
    def reset(id, drv_message)
553
        data      = decode(drv_message)
554
        host      = data.elements['HOST'].text
555
        deploy_id = data.elements['DEPLOY_ID'].text
556

    
557
        do_action("#{deploy_id} #{host}", id, host, ACTION[:reset])
558
    end
559

    
560
    #
561
    # ATTACHDISK action, attaches a disk to a running VM
562
    #
563
    def attach_disk(id, drv_message)
564
        action   = ACTION[:attach_disk]
565
        xml_data = decode(drv_message)
566

    
567
        tm_command = ensure_xpath(xml_data, id, action, 'TM_COMMAND') || return
568
        tm_rollback= xml_data.elements['TM_COMMAND_ROLLBACK'].text.strip
569

    
570
        target_xpath = "VM/TEMPLATE/DISK[ATTACH='YES']/TARGET"
571
        target     = ensure_xpath(xml_data, id, action, target_xpath) || return
572

    
573
        target_index = target.downcase[-1..-1].unpack('c').first - 97
574

    
575

    
576

    
577
        action = VmmAction.new(self, id, :attach_disk, drv_message)
578

    
579
        # Bug #1355, argument character limitation in ESX
580
        # Message not used in vmware anyway
581
        if @hypervisor == "vmware"
582
            drv_message = "drv_message"
583
        end
584

    
585
        steps = [
586
            # Perform a PROLOG on the disk
587
            {
588
                :driver     => :tm,
589
                :action     => :tm_attach,
590
                :parameters => tm_command.split
591
            },
592
            # Run the attach vmm script
593
            {
594
                :driver       => :vmm,
595
                :action       => :attach_disk,
596
                :parameters   => [
597
                        :deploy_id,
598
                        :disk_target_path,
599
                        target,
600
                        target_index,
601
                        drv_message
602
                ],
603
                :fail_actions => [
604
                    {
605
                        :driver     => :tm,
606
                        :action     => :tm_detach,
607
                        :parameters => tm_rollback.split
608
                    }
609
                ]
610
            }
611
        ]
612

    
613
        action.run(steps)
614
    end
615

    
616
    #
617
    # DETACHDISK action, attaches a disk to a running VM
618
    #
619
    def detach_disk(id, drv_message)
620
        action   = ACTION[:detach_disk]
621
        xml_data = decode(drv_message)
622

    
623
        tm_command = ensure_xpath(xml_data, id, action, 'TM_COMMAND') || return
624

    
625
        target_xpath = "VM/TEMPLATE/DISK[ATTACH='YES']/TARGET"
626
        target     = ensure_xpath(xml_data, id, action, target_xpath) || return
627

    
628
        target_index = target.downcase[-1..-1].unpack('c').first - 97
629

    
630
        action = VmmAction.new(self, id, :detach_disk, drv_message)
631

    
632
        steps = [
633
            # Run the detach vmm script
634
            {
635
                :driver       => :vmm,
636
                :action       => :detach_disk,
637
                :parameters   => [
638
                        :deploy_id,
639
                        :disk_target_path,
640
                        target,
641
                        target_index
642
                ]
643
            },
644
            # Perform an EPILOG on the disk
645
            {
646
                :driver     => :tm,
647
                :action     => :tm_detach,
648
                :parameters => tm_command.split
649
            }
650
        ]
651

    
652
        action.run(steps)
653
    end
654

    
655
    #
656
    # SNAPSHOTCREATE action, creates a new system snapshot
657
    #
658
    def snapshot_create(id, drv_message)
659
        action   = ACTION[:snapshot_create]
660
        xml_data = decode(drv_message)
661

    
662
        host      = xml_data.elements['HOST'].text
663
        deploy_id = xml_data.elements['DEPLOY_ID'].text
664

    
665
        snap_id_xpath = "VM/TEMPLATE/SNAPSHOT[ACTIVE='YES']/SNAPSHOT_ID"
666
        snap_id       = xml_data.elements[snap_id_xpath].text.to_i
667

    
668
        do_action("#{deploy_id} #{snap_id}",
669
                    id,
670
                    host,
671
                    ACTION[:snapshot_create],
672
                    :script_name => "snapshot_create")
673
    end
674

    
675
    #
676
    # SNAPSHOTREVERT action, reverts to a system snapshot
677
    #
678
    def snapshot_revert(id, drv_message)
679
        action   = ACTION[:snapshot_revert]
680
        xml_data = decode(drv_message)
681

    
682
        host      = xml_data.elements['HOST'].text
683
        deploy_id = xml_data.elements['DEPLOY_ID'].text
684

    
685
        snap_id_xpath = "VM/TEMPLATE/SNAPSHOT[ACTIVE='YES']/HYPERVISOR_ID"
686
        snapshot_name = xml_data.elements[snap_id_xpath].text
687

    
688
        do_action("#{deploy_id} #{snapshot_name}",
689
                    id,
690
                    host,
691
                    ACTION[:snapshot_revert],
692
                    :script_name => "snapshot_revert")
693
    end
694

    
695
    #
696
    # SNAPSHOTDELETE action, deletes a system snapshot
697
    #
698
    def snapshot_delete(id, drv_message)
699
        action   = ACTION[:snapshot_delete]
700
        xml_data = decode(drv_message)
701

    
702
        host      = xml_data.elements['HOST'].text
703
        deploy_id = xml_data.elements['DEPLOY_ID'].text
704

    
705
        snap_id_xpath = "VM/TEMPLATE/SNAPSHOT[ACTIVE='YES']/HYPERVISOR_ID"
706
        snapshot_name = xml_data.elements[snap_id_xpath].text
707

    
708
        do_action("#{deploy_id} #{snapshot_name}",
709
                    id,
710
                    host,
711
                    ACTION[:snapshot_delete],
712
                    :script_name => "snapshot_delete")
713
    end
714

    
715
    #
716
    # CLEANUP action, frees resources allocated in a host: VM and disk images
717
    #
718
    def cleanup(id, drv_message)
719
        aname    = ACTION[:cleanup]
720
        xml_data = decode(drv_message)
721

    
722
        tm_command = xml_data.elements['TM_COMMAND'].text
723
        mhost      = xml_data.elements['MIGR_HOST'].text
724
        deploy_id  = xml_data.elements['DEPLOY_ID'].text
725

    
726
        action = VmmAction.new(self, id, :cleanup, drv_message)
727
        steps  = Array.new
728

    
729
        # Cancel the VM at host (only if we have a valid deploy-id)
730
        if deploy_id && !deploy_id.empty?
731
            steps <<
732
            {
733
                :driver     => :vmm,
734
                :action     => :cancel,
735
                :parameters => [:deploy_id, :host],
736
                :no_fail    => true
737
            }
738
            steps <<
739
            {
740
                :driver  => :vnm,
741
                :action  => :clean,
742
                :no_fail => true
743
            }
744
        end
745

    
746
        # Cancel the VM at the previous host (in case of migration)
747
        if mhost && !mhost.empty?
748
            steps <<
749
            {
750
                :driver      => :vmm,
751
                :action      => :cancel,
752
                :parameters  => [:deploy_id, :dest_host],
753
                :destination => true,
754
                :no_fail     => true
755
            }
756
            steps <<
757
            {
758
                :driver  => :vnm,
759
                :action  => :clean,
760
                :destination => true,
761
                :no_fail => true
762
            }
763
        end
764

    
765
        # Cleans VM disk images and directory
766
        tm_command.each_line { |tc|
767
            tc.strip!
768

    
769
            steps <<
770
            {
771
                :driver     => :tm,
772
                :action     => :tm_delete,
773
                :parameters => tc.split,
774
                :no_fail    => true
775
            } if !tc.empty?
776
        } if tm_command
777

    
778
        action.run(steps)
779
    end
780

    
781
    #
782
    #  ATTACHNIC action to attach a new nic interface
783
    #
784
    def attach_nic(id, drv_message)
785
        xml_data = decode(drv_message)
786

    
787
        begin
788
            source = xml_data.elements["VM/TEMPLATE/NIC[ATTACH='YES']/BRIDGE"]
789
            mac    = xml_data.elements["VM/TEMPLATE/NIC[ATTACH='YES']/MAC"]
790
            nic_id = xml_data.elements["VM/TEMPLATE/NIC[ATTACH='YES']/NIC_ID"]
791

    
792
            source = source.text.strip
793
            mac    = mac.text.strip
794
           nic_id = nic_id.text.strip
795
        rescue
796
            send_message(action, RESULT[:failure], id,
797
                "Error in #{ACTION[:attach_nic]}, BRIDGE and MAC needed in NIC")
798
            return
799
        end
800

    
801
        model = xml_data.elements["VM/TEMPLATE/NIC[ATTACH='YES']/MODEL"]
802

    
803
        model = model.text if !model.nil?
804
        model = model.strip if !model.nil?
805
        model = "-" if model.nil?
806

    
807

    
808
        net_drv = xml_data.elements["NET_DRV"]
809

    
810
        net_drv = net_drv.text if !net_drv.nil?
811
        net_drv = net_drv.strip if !net_drv.nil?
812
        net_drv = "-" if net_drv.nil?
813

    
814
        action = VmmAction.new(self, id, :attach_nic, drv_message)
815

    
816
        steps=[
817
            # Execute pre-attach networking setup
818
            {
819
                :driver   => :vnm,
820
                :action   => :pre
821
            },
822
            # Attach the new NIC
823
            {
824
                :driver     => :vmm,
825
                :action     => :attach_nic,
826
                :parameters => [:deploy_id, mac, source, model, net_drv]
827
            },
828
            # Execute post-boot networking setup
829
            {
830
                :driver       => :vnm,
831
                :action       => :post,
832
                :parameters   => [:deploy_id, nic_id],
833
                :fail_actions => [
834
                    {
835
                        :driver     => :vmm,
836
                        :action     => :detach_nic,
837
                        :parameters => [:deploy_id, mac]
838
                    }
839
                ]
840
            }
841
        ]
842

    
843
        action.run(steps)
844
    end
845

    
846
    #
847
    #  DETACHNIC action to detach a nic interface
848
    #
849
    def detach_nic(id, drv_message)
850
        xml_data = decode(drv_message)
851

    
852
        begin
853
            mac    = xml_data.elements["VM/TEMPLATE/NIC[ATTACH='YES']/MAC"]
854
            nic_id = xml_data.elements["VM/TEMPLATE/NIC[ATTACH='YES']/NIC_ID"]
855

    
856
            mac    = mac.text.strip
857
            nic_id = nic_id.text.strip 
858
        rescue
859
            send_message(action, RESULT[:failure], id,
860
                "Error in #{ACTION[:detach_nic]}, MAC needed in NIC")
861
            return
862
        end
863

    
864
        action = VmmAction.new(self, id, :detach_nic, drv_message)
865

    
866
        steps=[
867
            # Detach the NIC
868
            {
869
                :driver     => :vmm,
870
                :action     => :detach_nic,
871
                :parameters => [:deploy_id, mac]
872
            },
873
            # Clean networking setup
874
            {
875
                :driver       => :vnm,
876
                :action       => :clean,
877
                :parameters   => [nic_id]
878
            }
879
        ]
880

    
881
        action.run(steps)
882
    end
883

    
884
private
885

    
886
    def ensure_xpath(xml_data, id, action, xpath)
887
        begin
888
            value = xml_data.elements[xpath].text.strip
889
            raise if value.empty?
890
            value
891
        rescue
892
            send_message(action, RESULT[:failure], id,
893
                "Cannot perform #{action}, expecting #{xpath}")
894
            nil
895
        end
896
    end
897

    
898
end
899

    
900
################################################################################
901
#
902
# Virtual Machine Manager Execution Driver - Main Program
903
#
904
################################################################################
905

    
906
opts = GetoptLong.new(
907
    [ '--retries',    '-r', GetoptLong::OPTIONAL_ARGUMENT ],
908
    [ '--threads',    '-t', GetoptLong::OPTIONAL_ARGUMENT ],
909
    [ '--local',      '-l', GetoptLong::REQUIRED_ARGUMENT ],
910
    [ '--shell',      '-s', GetoptLong::REQUIRED_ARGUMENT ],
911
    [ '--parallel',   '-p', GetoptLong::NO_ARGUMENT ]
912
)
913

    
914
hypervisor      = ''
915
retries         = 0
916
threads         = 15
917
shell           = 'bash'
918
local_actions   = {}
919
single_host     = true
920

    
921
begin
922
    opts.each do |opt, arg|
923
        case opt
924
            when '--retries'
925
                retries   = arg.to_i
926
            when '--threads'
927
                threads   = arg.to_i
928
            when '--local'
929
                local_actions=OpenNebulaDriver.parse_actions_list(arg)
930
            when '--shell'
931
                shell   = arg
932
            when '--parallel'
933
                single_host = false
934
        end
935
    end
936
rescue Exception => e
937
    exit(-1)
938
end
939

    
940
if ARGV.length >= 1
941
    hypervisor = ARGV.shift
942
else
943
    exit(-1)
944
end
945

    
946
exec_driver = ExecDriver.new(hypervisor,
947
                :concurrency => threads,
948
                :retries => retries,
949
                :local_actions => local_actions,
950
                :shell => shell,
951
                :single_host => single_host)
952

    
953
exec_driver.start_driver