Revision 6ab1f325

View differences:

src/vmm_mad/remotes/lib/vcenter_driver/datacenter.rb
349 349
class Datacenter
350 350
    attr_accessor :item
351 351

  
352
    DPG_CREATE_TIMEOUT = 240
353

  
352 354
    def initialize(item, vi_client=nil)
353 355
        if !item.instance_of? RbVmomi::VIM::Datacenter
354 356
            raise "Expecting type 'RbVmomi::VIM::Datacenter'. " <<
......
357 359

  
358 360
        @vi_client = vi_client
359 361
        @item = item
362
        @net_rollback = []
363
        @locking = true
360 364
    end
361 365

  
362 366
    def datastore_folder
......
375 379
        NetworkFolder.new(@item.networkFolder)
376 380
    end
377 381

  
382
    # Locking function. Similar to flock
383
    def lock
384
        hostlockname = @item['name'].downcase.tr(" ", "_")
385
        if @locking
386
           @locking_file = File.open("/tmp/vcenter-dc-#{hostlockname}-lock","w")
387
           @locking_file.flock(File::LOCK_EX)
388
        end
389
    end
390

  
391
    # Unlock driver execution mutex
392
    def unlock
393
        if @locking
394
            @locking_file.close
395
        end
396
    end
397

  
398
    ########################################################################
399
    # Check if distributed virtual switch exists in host
400
    ########################################################################
401
    def dvs_exists(switch_name, net_folder)
402

  
403
        return net_folder.items.values.select{ |dvs|
404
            dvs.instance_of?(VCenterDriver::DistributedVirtualSwitch) &&
405
            dvs['name'] == switch_name
406
        }.first rescue nil
407
    end
408

  
409
    ########################################################################
410
    # Is the distributed switch for the distributed pg different?
411
    ########################################################################
412
    def pg_changes_sw?(dpg, switch_name)
413
        return dpg['config.distributedVirtualSwitch.name'] != switch_name
414
    end
415

  
416
    ########################################################################
417
    # Create a distributed vcenter switch in a datacenter
418
    ########################################################################
419
    def create_dvs(switch_name, pnics, mtu=1500)
420
        # Prepare spec for DVS creation
421
        spec = RbVmomi::VIM::DVSCreateSpec.new
422
        spec.configSpec = RbVmomi::VIM::VMwareDVSConfigSpec.new
423
        spec.configSpec.name = switch_name
424

  
425
        # Specify number of uplinks port for dpg
426
        if pnics
427
            pnics = pnics.split(",")
428
            if !pnics.empty?
429
                spec.configSpec.uplinkPortPolicy = RbVmomi::VIM::DVSNameArrayUplinkPortPolicy.new
430
                spec.configSpec.uplinkPortPolicy.uplinkPortName = []
431
                (0..pnics.size-1).each { |index|
432
                    spec.configSpec.uplinkPortPolicy.uplinkPortName[index]="dvUplink#{index+1}"
433
                }
434
            end
435
        end
436

  
437
        #Set maximum MTU
438
        spec.configSpec.maxMtu = mtu
439

  
440
        # The DVS must be created in the networkFolder of the datacenter
441
        begin
442
            dvs_creation_task = @item.networkFolder.CreateDVS_Task(:spec => spec)
443
            dvs_creation_task.wait_for_completion
444

  
445
            # If task finished successfuly we rename the uplink portgroup
446
            dvs = nil
447
            if dvs_creation_task.info.state == 'success'
448
                dvs = dvs_creation_task.info.result
449
                dvs.config.uplinkPortgroup[0].Rename_Task(:newName => "#{switch_name}-uplink-pg").wait_for_completion
450
            else
451
                raise "The Distributed vSwitch #{switch_name} could not be created. "
452
            end
453
        rescue Exception => e
454
            raise e
455
        end
456

  
457
        @net_rollback << {:action => :delete_dvs, :dvs => dvs, :name => switch_name}
458

  
459
        return VCenterDriver::DistributedVirtualSwitch.new(dvs, @vi_client)
460
    end
461

  
462
    ########################################################################
463
    # Update a distributed vcenter switch
464
    ########################################################################
465
    def update_dvs(dvs, pnics, mtu)
466
        # Prepare spec for DVS creation
467
        spec = RbVmomi::VIM::VMwareDVSConfigSpec.new
468
        changed = false
469

  
470
        orig_spec = RbVmomi::VIM::VMwareDVSConfigSpec.new
471
        orig_spec.maxMtu = dvs['config.maxMtu']
472
        orig_spec.uplinkPortPolicy = RbVmomi::VIM::DVSNameArrayUplinkPortPolicy.new
473
        orig_spec.uplinkPortPolicy.uplinkPortName = []
474
        (0..dvs['config.uplinkPortgroup'].length-1).each { |index|
475
                orig_spec.uplinkPortPolicy.uplinkPortName[index]="dvUplink#{index+1}"
476
        }
477

  
478
        # Add more uplinks to default uplink port group according to number of pnics
479
        if pnics
480
            pnics = pnics.split(",")
481
            if !pnics.empty? && dvs['config.uplinkPortgroup'].length != pnics.size
482
                spec.uplinkPortPolicy = RbVmomi::VIM::DVSNameArrayUplinkPortPolicy.new
483
                spec.uplinkPortPolicy.uplinkPortName = []
484
                (dvs['config.uplinkPortgroup'].length..num_pnics-1).each { |index|
485
                    spec.uplinkPortPolicy.uplinkPortName[index]="dvUplink#{index+1}"
486
                }
487
                changed = true
488
            end
489
        end
490

  
491
        #Set maximum MTU
492
        if mtu != dvs['config.maxMtu']
493
            spec.maxMtu = mtu
494
            changed = true
495
        end
496

  
497
        # The DVS must be created in the networkFolder of the datacenter
498
        if changed
499
            spec.configVersion = dvs['config.configVersion']
500

  
501
            begin
502
                dvs.item.ReconfigureDvs_Task(:spec => spec).wait_for_completion
503
            rescue Exception => e
504
                raise "The Distributed switch #{dvs['name']} could not be updated. "\
505
                      "Reason: #{e.message}"
506
            end
507

  
508
            @net_rollback << {:action => :update_dvs, :dvs => dvs.item, :name => dvs['name'], :spec => orig_spec}
509
        end
510
    end
511

  
512
    ########################################################################
513
    # Remove a distributed vcenter switch in a datacenter
514
    ########################################################################
515
    def remove_dvs(dvs)
516
        begin
517
            dvs.item.Destroy_Task.wait_for_completion
518
        rescue
519
            #Ignore destroy task exception
520
        end
521
    end
522

  
523
    ########################################################################
524
    # Check if distributed port group exists in datacenter
525
    ########################################################################
526
    def dpg_exists(pg_name, net_folder)
527

  
528
        return net_folder.items.values.select{ |dpg|
529
            dpg.instance_of?(VCenterDriver::DistributedPortGroup) &&
530
            dpg['name'] == pg_name
531
        }.first rescue nil
532
    end
533

  
534
    ########################################################################
535
    # Create a distributed vcenter port group
536
    ########################################################################
537
    def create_dpg(dvs, pg_name, vlan_id, num_ports)
538
        spec = RbVmomi::VIM::DVPortgroupConfigSpec.new
539

  
540
        # OpenNebula use DVS static port binding with autoexpand
541
        if num_ports
542
            spec.autoExpand = true
543
            spec.numPorts = num_ports
544
        end
545

  
546
        # Distributed port group name
547
        spec.name = pg_name
548

  
549
        # Set VLAN information
550
        spec.defaultPortConfig = RbVmomi::VIM::VMwareDVSPortSetting.new
551
        spec.defaultPortConfig.vlan = RbVmomi::VIM::VmwareDistributedVirtualSwitchVlanIdSpec.new
552
        spec.defaultPortConfig.vlan.vlanId = vlan_id
553
        spec.defaultPortConfig.vlan.inherited = false
554

  
555
        # earlyBinding. A free DistributedVirtualPort will be selected and
556
        # assigned to a VirtualMachine when the virtual machine is reconfigured
557
        # to connect to the portgroup.
558
        spec.type = "earlyBinding"
559

  
560
        begin
561
            dvs.item.AddDVPortgroup_Task(spec: [spec]).wait_for_completion
562
        rescue Exception => e
563
            raise "The Distributed port group #{pg_name} could not be created. "\
564
                  "Reason: #{e.message}"
565
        end
566

  
567
        # wait until the network is ready and we have a reference
568
        portgroups = dvs['portgroup'].select{ |dpg|
569
            dpg.instance_of?(RbVmomi::VIM::DistributedVirtualPortgroup) &&
570
            dpg['name'] == pg_name
571
        }
572

  
573
        (0..DPG_CREATE_TIMEOUT).each do
574
            break if !portgroups.empty?
575
            portgroups = dvs['portgroup'].select{ |dpg|
576
                dpg.instance_of?(RbVmomi::VIM::DistributedVirtualPortgroup) &&
577
                dpg['name'] == pg_name
578
            }
579
            sleep 1
580
        end
581

  
582
        raise "Cannot get VCENTER_NET_REF for new distributed port group" if portgroups.empty?
583

  
584
        @net_rollback << {:action => :delete_dpg, :dpg => portgroups.first, :name => pg_name}
585

  
586
        return portgroups.first._ref
587
    end
588

  
589
    ########################################################################
590
    # Update a distributed vcenter port group
591
    ########################################################################
592
    def update_dpg(dpg, vlan_id, num_ports)
593
        spec = RbVmomi::VIM::DVPortgroupConfigSpec.new
594

  
595
        changed = false
596

  
597
        orig_spec = RbVmomi::VIM::DVPortgroupConfigSpec.new
598
        orig_spec.numPorts = dpg['config.numPorts']
599
        orig_spec.defaultPortConfig = RbVmomi::VIM::VMwareDVSPortSetting.new
600
        orig_spec.defaultPortConfig.vlan = RbVmomi::VIM::VmwareDistributedVirtualSwitchVlanIdSpec.new
601
        orig_spec.defaultPortConfig.vlan.vlanId = dpg['config.defaultPortConfig.vlan.vlanId']
602
        orig_spec.defaultPortConfig.vlan.inherited = false
603

  
604
        if num_ports && num_ports != orig_spec.numPorts
605
            spec.numPorts = num_ports
606
            changed = true
607
        end
608

  
609
        # earlyBinding. A free DistributedVirtualPort will be selected and
610
        # assigned to a VirtualMachine when the virtual machine is reconfigured
611
        # to connect to the portgroup.
612
        spec.type = "earlyBinding"
613

  
614
        if vlan_id != orig_spec.defaultPortConfig.vlan.vlanId
615
            spec.defaultPortConfig = RbVmomi::VIM::VMwareDVSPortSetting.new
616
            spec.defaultPortConfig.vlan = RbVmomi::VIM::VmwareDistributedVirtualSwitchVlanIdSpec.new
617
            spec.defaultPortConfig.vlan.vlanId = vlan_id
618
            spec.defaultPortConfig.vlan.inherited = false
619
            changed = true
620
        end
621

  
622
        if changed
623

  
624
            spec.configVersion = dpg['config.configVersion']
625

  
626
            begin
627
                dpg.item.ReconfigureDVPortgroup_Task(:spec => spec).wait_for_completion
628
            rescue Exception => e
629
                raise "The Distributed port group #{dpg['name']} could not be created. "\
630
                      "Reason: #{e.message}"
631
            end
632

  
633
            @net_rollback << {:action => :update_dpg, :dpg => dpg.item, :name => dpg['name'], :spec => orig_spec}
634
        end
635

  
636
    end
637

  
638
    ########################################################################
639
    # Remove distributed port group from datacenter
640
    ########################################################################
641
    def remove_dpg(dpg)
642
        begin
643
            dpg.item.Destroy_Task.wait_for_completion
644
        rescue RbVmomi::VIM::ResourceInUse => e
645
            STDERR.puts "The distributed portgroup #{dpg["name"]} is in use so it cannot be deleted"
646
            return nil
647
        rescue Exception => e
648
            raise "The Distributed portgroup #{dpg["name"]} could not be deleted. Reason: #{e.message} "
649
        end
650
    end
651

  
652
    ########################################################################
653
    # Perform vcenter network rollback operations
654
    ########################################################################
655
    def network_rollback
656
        @net_rollback.reverse_each do |nr|
657

  
658
            case nr[:action]
659
                when :update_dpg
660
                    begin
661
                        nr[:dpg].ReconfigureDVPortgroupConfigSpec_Task(:spec => nr[:spec])
662
                    rescue Exception => e
663
                        raise "A rollback operation for distributed port group #{nr[:name]} could not be performed. Reason: #{e.message}"
664
                    end
665
                when :update_dvs
666
                    begin
667
                        nr[:dvs].ReconfigureDvs_Task(:spec => nr[:spec])
668
                    rescue Exception => e
669
                        raise "A rollback operation for distributed standard switch #{nr[:name]} could not be performed. Reason: #{e.message}"
670
                    end
671
                when :delete_dvs
672
                    begin
673
                        nr[:dvs].Destroy_Task.wait_for_completion
674
                    rescue RbVmomi::VIM::ResourceInUse
675
                        return #Ignore if switch in use
676
                    rescue RbVmomi::VIM::NotFound
677
                        return #Ignore if switch not found
678
                    rescue Exception => e
679
                        raise "A rollback operation for standard switch #{nr[:name]} could not be performed. Reason: #{e.message}"
680
                    end
681
                when :delete_dpg
682
                    begin
683
                        nr[:dpg].Destroy_Task.wait_for_completion
684
                    rescue RbVmomi::VIM::ResourceInUse
685
                        return #Ignore if pg in use
686
                    rescue RbVmomi::VIM::NotFound
687
                        return #Ignore if pg not found
688
                    rescue Exception => e
689
                        raise "A rollback operation for standard port group #{nr[:name]} could not be performed. Reason: #{e.message}"
690
                    end
691
            end
692
        end
693
    end
694

  
378 695
    def self.new_from_ref(ref, vi_client)
379 696
        self.new(RbVmomi::VIM::Datacenter.new(vi_client.vim, ref), vi_client)
380 697
    end

Also available in: Unified diff