Revision 0db55e1c src/vmm_mad/remotes/lib/vcenter_driver/virtual_machine.rb

View differences:

src/vmm_mad/remotes/lib/vcenter_driver/virtual_machine.rb
155 155
        end.first.value rescue nil
156 156
    end
157 157

  
158
    def get_vcenter_instance_uuid
159
        @vi_client.vim.serviceContent.about.instanceUuid
160
    end
161

  
158 162
    ############################################################################
159 163
    # Getters
160 164
    ############################################################################
......
504 508
    def reconfigure
505 509
        extraconfig   = []
506 510
        device_change = []
511
        networks      = {}
512

  
513
        npool = VCenterDriver::VIHelper.one_pool(OpenNebula::VirtualNetworkPool, false)
507 514

  
508 515
        # get vmid
509 516
        extraconfig += extraconfig_vmid
......
514 521
        # vnc configuration (for config_array hash)
515 522
        extraconfig += extraconfig_vnc
516 523

  
524
        # prepare pg and sw for vcenter nics if any
525
        configure_vcenter_network
526

  
517 527
        # device_change hash (nics)
518 528
        device_change += device_change_nics
519 529

  
520
        # device_change hash (disks)
521
        device_change += device_change_disks
530
        # track pg or dpg in case they must be removed
531
        vcenter_uuid = get_vcenter_instance_uuid
532
        device_change.each do |nic|
533
            if nic[:operation] == :remove
522 534

  
523
        num_cpus = one_item["TEMPLATE/VCPU"] || 1
535
                vnet_ref = nil
524 536

  
525
        spec_hash = {
526
            :numCPUs      => num_cpus.to_i,
527
            :memoryMB     => one_item["TEMPLATE/MEMORY"],
528
            :extraConfig  => extraconfig
529
        }
530

  
531
        spec_hash[:deviceChange] = device_change if !device_change.empty?
537
                if nic[:device].backing.respond_to?(:network)
538
                    vnet_ref = nic[:device].backing.network._ref
539
                end
532 540

  
533
        spec = RbVmomi::VIM.VirtualMachineConfigSpec(spec_hash)
541
                if nic[:device].backing.respond_to?(:port) &&
542
                   nic[:device].backing.port.respond_to?(:portgroupKey)
543
                    vnet_ref  = nic[:device].backing.port.portgroupKey
544
                end
534 545

  
535
        @item.ReconfigVM_Task(:spec => spec).wait_for_completion
546
                one_network = VCenterDriver::VIHelper.find_by_ref(OpenNebula::VirtualNetworkPool,
547
                                                                  "TEMPLATE/VCENTER_NET_REF",
548
                                                                  vnet_ref,
549
                                                                  vcenter_uuid,
550
                                                                  npool)
551
                next if !one_network
552
                if one_network["VN_MAD"] == "vcenter" && !networks.key?(one_network["BRIDGE"])
553
                    networks[one_network["BRIDGE"]] = one_network
554
                end
555
            end
536 556
    end
537 557

  
538 558
    def extraconfig_vmid
......
628 648
        end
629 649
    end
630 650

  
631
 # Returns an array of actions to be included in :deviceChange
651
    # Returns an array of actions to be included in :deviceChange
632 652
    def calculate_add_nic_spec(nic)
633 653

  
634 654
        #TODO include VCENTER_NET_MODEL usage it should be in one_item
635 655
        mac       = nic["MAC"]
636
        bridge    = nic["BRIDGE"]
637
        model     = nic["VCENTER_NET_MODEL"]
656
        pg_name   = nic["BRIDGE"]
657
        model     = nic["VCENTER_NET_MODEL"] || VCenterDriver::VIHelper.get_default("VM/TEMPLATE/NIC/MODEL")
638 658
        vnet_ref  = nic["VCENTER_NET_REF"]
639 659
        backing   = nil
640 660

  
641
        limit_in  = nic["INBOUND_PEAK_BW"]
661
        limit_in  = nic["INBOUND_PEAK_BW"] || VCenterDriver::VIHelper.get_default("VM/TEMPLATE/NIC/INBOUND_PEAK_BW")
642 662
        limit_out = nic["OUTBOUND_PEAK_BW"]
643 663
        limit     = nil
644 664

  
......
654 674
            rsrv=([rsrv_in.to_i, rsrv_out.to_i].min / 1024) * 8
655 675
        end
656 676

  
657
        network = self["runtime.host.network"].select do |n|
658
            n._ref == vnet_ref
677
        network = self["runtime.host"].network.select do |n|
678
            n._ref == vnet_ref || n.name == pg_name
659 679
        end
660 680

  
661
        if network.empty?
662
            raise "Network #{bridge} not found in host #{self['runtime.host.name']}"
663
        else
664
            network = network.first
665
        end
681
        network = network.first
666 682

  
667 683
        card_num = 1 # start in one, we want the next avaliable id
668 684

  
......
691 707

  
692 708
        if network.class == RbVmomi::VIM::Network
693 709
            backing = RbVmomi::VIM.VirtualEthernetCardNetworkBackingInfo(
694
                        :deviceName => bridge,
710
                        :deviceName => pg_name,
695 711
                        :network    => network)
696 712
        else
697 713
            port    = RbVmomi::VIM::DistributedVirtualSwitchPortConnection(
......
707 723
            :key => 0,
708 724
            :deviceInfo => {
709 725
                :label => "net" + card_num.to_s,
710
                :summary => bridge
726
                :summary => pg_name
711 727
            },
712 728
            :backing     => backing,
713 729
            :addressType => mac ? 'manual' : 'generated',
......
733 749
        }
734 750
    end
735 751

  
752
    def vcenter_standard_network(nic, esx_host, vcenter_uuid)
753
        pg_name     = nic["BRIDGE"]
754
        switch_name = nic["VCENTER_SWITCH_NAME"]
755
        pnics       = nic["PHYDEV"] || nil
756
        mtu         = nic["MTU"] || 1500
757
        vlan_id     = nic["VLAN_ID"] || nic["AUTOMATIC_VLAN_ID"] || 0
758
        num_ports   = nic["VCENTER_SWITCH_NPORTS"] || 128
759

  
760
        begin
761
            esx_host.lock # Exclusive lock for ESX host operation
762

  
763
            pnics_available = nil
764
            pnics_available = esx_host.get_available_pnics if pnics
765

  
766
            # Get port group if it exists
767
            pg = esx_host.pg_exists(pg_name)
768

  
769
            # Disallow changes of switch name for existing pg
770
            if pg && esx_host.pg_changes_sw?(pg, switch_name)
771
                raise "The port group's switch name can not be modified"\
772
                    " for OpenNebula's virtual network, please revert"\
773
                    " it back in its definition and create a different"\
774
                    " virtual network instead."
775
            end
776

  
777
            if !pg
778
                # Get standard switch if it exists
779
                vs = esx_host.vss_exists(switch_name)
780

  
781
                if !vs
782
                    switch_name = esx_host.create_vss(switch_name, pnics, num_ports, mtu, pnics_available)
783
                else
784
                    #Update switch
785
                    esx_host.update_vss(vs, switch_name, pnics, num_ports, mtu)
786
                end
787

  
788
                vnet_ref     = esx_host.create_pg(pg_name, switch_name, vlan_id)
789

  
790
                # We must update XML so the VCENTER_NET_REF is set
791
                one_vnet = VCenterDriver::VIHelper.one_item(OpenNebula::VirtualNetwork, nic["NETWORK_ID"])
792
                one_vnet.delete_element("TEMPLATE/VCENTER_NET_REF") if one_vnet["TEMPLATE/VCENTER_NET_REF"]
793
                one_vnet.delete_element("TEMPLATE/VCENTER_INSTANCE_ID") if one_vnet["TEMPLATE/VCENTER_INSTANCE_ID"]
794
                rc = one_vnet.update("VCENTER_NET_REF = \"#{vnet_ref}\"\n"\
795
                                        "VCENTER_INSTANCE_ID = \"#{vcenter_uuid}\"", true)
796
                if OpenNebula.is_error?(rc)
797
                    raise "Could not update VCENTER_NET_REF for virtual network"
798
                end
799
                one_vnet.info
800

  
801
            else
802
                # pg exist, update
803
                esx_host.update_pg(pg, switch_name, vlan_id)
804

  
805
                # update switch if needed
806
                vs = esx_host.vss_exists(switch_name)
807
                esx_host.update_vss(vs, switch_name, pnics, num_ports, mtu) if vs
808
            end
809

  
810
        rescue Exception => e
811
            esx_host.network_rollback
812
            raise e
813
        ensure
814
            esx_host.unlock if esx_host # Remove lock
815
        end
816
    end
817

  
818
    def vcenter_distributed_network(nic, esx_host, vcenter_uuid, dc, net_folder)
819
        pg_name     = nic["BRIDGE"]
820
        switch_name = nic["VCENTER_SWITCH_NAME"]
821
        pnics       = nic["PHYDEV"] || nil
822
        mtu         = nic["MTU"] || 1500
823
        vlan_id     = nic["VLAN_ID"] || nic["AUTOMATIC_VLAN_ID"] || 0
824
        num_ports   = nic["VCENTER_SWITCH_NPORTS"] || 8
825

  
826
        begin
827
            # Get distributed port group if it exists
828
            dpg = dc.dpg_exists(pg_name, net_folder)
829

  
830
            # Disallow changes of switch name for existing pg
831
            if dpg && dc.pg_changes_sw?(dpg, switch_name)
832
                raise "The port group's switch name can not be modified"\
833
                    " for OpenNebula's virtual network, please revert"\
834
                    " it back in its definition and create a different"\
835
                    " virtual network instead."
836
            end
837

  
838
            if !dpg
839
                # Get distributed virtual switch if it exists
840
                dvs = dc.dvs_exists(switch_name, net_folder)
841

  
842
                if !dvs
843
                    dvs = dc.create_dvs(switch_name, pnics, mtu)
844
                else
845
                    #Update switch
846
                    dc.update_dvs(dvs, pnics, mtu)
847
                end
848

  
849
                vnet_ref = dc.create_dpg(dvs, pg_name, vlan_id, num_ports)
850

  
851
                # We must connect portgroup to current host
852
                begin
853
                    esx_host.lock
854

  
855
                    pnics_available = nil
856
                    pnics_available = esx_host.get_available_pnics if pnics
857

  
858
                    proxy_switch = esx_host.proxy_switch_exists(switch_name)
859

  
860
                    esx_host.assign_proxy_switch(dvs, switch_name, pnics, pnics_available)
861

  
862
                rescue Exception => e
863
                    raise e
864
                ensure
865
                    esx_host.unlock if esx_host # Remove lock
866
                end
867

  
868
                # We must update XML so the VCENTER_NET_REF is set
869
                one_vnet = VCenterDriver::VIHelper.one_item(OpenNebula::VirtualNetwork, nic["NETWORK_ID"])
870
                one_vnet.delete_element("TEMPLATE/VCENTER_NET_REF") if one_vnet["TEMPLATE/VCENTER_NET_REF"]
871
                one_vnet.delete_element("TEMPLATE/VCENTER_INSTANCE_ID") if one_vnet["TEMPLATE/VCENTER_INSTANCE_ID"]
872
                rc = one_vnet.update("VCENTER_NET_REF = \"#{vnet_ref}\"\n"\
873
                                        "VCENTER_INSTANCE_ID = \"#{vcenter_uuid}\"", true)
874
                if OpenNebula.is_error?(rc)
875
                    raise "Could not update VCENTER_NET_REF for virtual network"
876
                end
877
                one_vnet.info
878
            else
879
                # pg exist, dpg update
880
                dc.update_dpg(dpg, vlan_id, num_ports)
881

  
882
                # update switch if needed
883
                dvs = dc.dvs_exists(switch_name, net_folder)
884
                dc.update_dvs(dvs, pnics, mtu) if dvs
885

  
886
                # We must connect or update portgroup to current host (proxyswitch)
887
                begin
888
                    esx_host.lock
889

  
890
                    pnics_available = nil
891
                    pnics_available = esx_host.get_available_pnics if pnics
892

  
893
                    proxy_switch = esx_host.proxy_switch_exists(switch_name)
894
                    esx_host.assign_proxy_switch(dvs, switch_name, pnics, pnics_available)
895

  
896
                rescue Exception => e
897
                    raise e
898
                ensure
899
                    esx_host.unlock if esx_host # Remove lock
900
                end
901
            end
902

  
903
        rescue Exception => e
904
            dc.network_rollback
905
            raise e
906
        end
907

  
908
    end
909

  
910
    def configure_vcenter_network(nic_xml=nil)
911
        nics = []
912
        if nic_xml
913
            nics << nic_xml
914
        else
915
            nics = one_item.retrieve_xmlelements("TEMPLATE/NIC[VN_MAD=\"vcenter\"]")
916
        end
917

  
918
        return if nics.empty?
919

  
920
        vcenter_uuid = get_vcenter_instance_uuid
921
        esx_host = VCenterDriver::ESXHost.new_from_ref(self['runtime'].host._ref, vi_client)
922

  
923
        nics.each do |nic|
924

  
925
            if nic["VCENTER_INSTANCE_ID"] && nic["VCENTER_INSTANCE_ID"] != vcenter_uuid
926
                raise "The virtual network is not assigned to the right vcenter server, create a different virtual network instead"
927
            end
928

  
929
            if nic["VCENTER_PORTGROUP_TYPE"] == "Port Group"
930
                vcenter_standard_network(nic, esx_host, vcenter_uuid)
931
            end
932

  
933
            if nic["VCENTER_PORTGROUP_TYPE"] == "Distributed Port Group"
934
                dc = cluster.get_dc # Get datacenter
935
                begin
936
                    dc.lock
937

  
938
                    # Explore network folder in search of dpg and dvs
939
                    net_folder = dc.network_folder
940
                    net_folder.fetch!
941

  
942
                    vcenter_distributed_network(nic, esx_host, vcenter_uuid, dc, net_folder)
943
                rescue Exception => e
944
                    #TODO rollback
945
                    raise e
946
                ensure
947
                    dc.unlock if dc
948
                end
949
            end
950
        end
951
    end
952

  
736 953
    # Add NIC to VM
737 954
    def attach_nic
738 955
        spec_hash = {}
......
741 958
        # Extract nic from driver action
742 959
        nic = one_item.retrieve_xmlelements("TEMPLATE/NIC[ATTACH='YES']").first
743 960

  
744
        # A new NIC requires a vcenter spec
745
        attach_nic_array = []
746
        attach_nic_array << calculate_add_nic_spec(nic)
747
        spec_hash[:deviceChange] = attach_nic_array if !attach_nic_array.empty?
961
        begin
962
            # Prepare network for vcenter networks
963
            configure_vcenter_network(nic) if nic["VN_MAD"] == "vcenter"
748 964

  
749
        # Reconfigure VM
750
        spec = RbVmomi::VIM.VirtualMachineConfigSpec(spec_hash)
965
            # A new NIC requires a vcenter spec
966
            attach_nic_array = []
967
            attach_nic_array << calculate_add_nic_spec(nic)
968
            spec_hash[:deviceChange] = attach_nic_array if !attach_nic_array.empty?
969

  
970
            # Reconfigure VM
971
            spec = RbVmomi::VIM.VirtualMachineConfigSpec(spec_hash)
751 972

  
752
        begin
753 973
            @item.ReconfigVM_Task(:spec => spec).wait_for_completion
754 974
        rescue Exception => e
755 975
            raise "Cannot attach NIC to VM: #{e.message}\n#{e.backtrace}"
......
757 977

  
758 978
    end
759 979

  
760
 # Detach NIC from VM
980
    # Detach NIC from VM
761 981
    def detach_nic
762 982
        spec_hash = {}
763 983
        nic = nil
......
771 991
            is_nic?(device) && (device.macAddress ==  mac)
772 992
        end rescue nil
773 993

  
774
        raise "Could not find NIC with mac address #{mac}" if nic_device.nil?
994
        return if nic_device.nil? #Silently ignore if nic is not found
775 995

  
776 996
        # Remove NIC from VM in the ReconfigVM_Task
777 997
        spec_hash[:deviceChange] = [
......
783 1003
        rescue Exception => e
784 1004
            raise "Cannot detach NIC from VM: #{e.message}\n#{e.backtrace}"
785 1005
        end
1006
    end
1007

  
1008
    # Detach all nics useful when removing pg and sw so they're not in use
1009
    def detach_all_nics
1010
        spec_hash = {}
1011
        device_change = []
1012

  
1013
        @item["config.hardware.device"].each do |device|
1014
            if is_nic?(device)
1015
                device_change << {:operation => :remove, :device => device}
1016
            end
1017
        end
786 1018

  
1019
        # Remove NIC from VM in the ReconfigVM_Task
1020
        spec_hash[:deviceChange] = device_change
1021

  
1022
        begin
1023
            @item.ReconfigVM_Task(:spec => spec_hash).wait_for_completion
1024
        rescue Exception => e
1025
            raise "Cannot detach all NICs from VM: #{e.message}\n#{e.backtrace}"
1026
        end
787 1027
    end
788 1028

  
789 1029
    #  Checks if a RbVmomi::VIM::VirtualDevice is a network interface

Also available in: Unified diff