5.0_boot_devices.rb
| 1 | 
      # coding: utf-8
     | 
  
|---|---|
| 2 | 
      # -------------------------------------------------------------------------- #
     | 
  
| 3 | 
      # Copyright 2002-2016, OpenNebula Project, OpenNebula Systems                #
     | 
  
| 4 | 
      #                                                                            #
     | 
  
| 5 | 
      # Licensed under the Apache License, Version 2.0 (the "License"); you may    #
     | 
  
| 6 | 
      # not use this file except in compliance with the License. You may obtain    #
     | 
  
| 7 | 
      # a copy of the License at                                                   #
     | 
  
| 8 | 
      #                                                                            #
     | 
  
| 9 | 
      # http://www.apache.org/licenses/LICENSE-2.0                                 #
     | 
  
| 10 | 
      #                                                                            #
     | 
  
| 11 | 
      # Unless required by applicable law or agreed to in writing, software        #
     | 
  
| 12 | 
      # distributed under the License is distributed on an "AS IS" BASIS,          #
     | 
  
| 13 | 
      # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
     | 
  
| 14 | 
      # See the License for the specific language governing permissions and        #
     | 
  
| 15 | 
      # limitations under the License.                                             #
     | 
  
| 16 | 
      #--------------------------------------------------------------------------- #
     | 
  
| 17 | 
       | 
  
| 18 | 
      require 'nokogiri'
     | 
  
| 19 | 
       | 
  
| 20 | 
      module OneDBPatch  | 
  
| 21 | 
      VERSION = "4.90.0"  | 
  
| 22 | 
      LOCAL_VERSION = "4.90.0"  | 
  
| 23 | 
       | 
  
| 24 | 
      IMAGE_TYPE = %w[hd cdrom]  | 
  
| 25 | 
       | 
  
| 26 | 
      class NoImageError < StandardError  | 
  
| 27 | 
        end
     | 
  
| 28 | 
       | 
  
| 29 | 
      def is_hot_patch(ops)  | 
  
| 30 | 
      return false  | 
  
| 31 | 
        end
     | 
  
| 32 | 
       | 
  
| 33 | 
      def check_db_version(ops)  | 
  
| 34 | 
      db_version = read_db_version()  | 
  
| 35 | 
       | 
  
| 36 | 
      if ( db_version[:version] != VERSION ||  | 
  
| 37 | 
      db_version[:local_version] != LOCAL_VERSION )  | 
  
| 38 | 
       | 
  
| 39 | 
      raise <<-EOT  | 
  
| 40 | 
      Version mismatch: patch file is for version
     | 
  
| 41 | 
      Shared: #{VERSION}, Local: #{LOCAL_VERSION}
     | 
  
| 42 | 
      
     | 
  
| 43 | 
      Current database is version
     | 
  
| 44 | 
      Shared: #{db_version[:version]}, Local: #{db_version[:local_version]}
     | 
  
| 45 | 
      EOT
     | 
  
| 46 | 
          end
     | 
  
| 47 | 
        end
     | 
  
| 48 | 
       | 
  
| 49 | 
        # Modify template boot order to match disk# and nic# instead of HD, CDROM and NETWORK
     | 
  
| 50 | 
        # Params:
     | 
  
| 51 | 
        # +ops+:: Options
     | 
  
| 52 | 
      def patch(ops)  | 
  
| 53 | 
      init_log_time()  | 
  
| 54 | 
       | 
  
| 55 | 
          #
     | 
  
| 56 | 
       | 
  
| 57 | 
      @db.transaction do  | 
  
| 58 | 
      @db.fetch("SELECT oid,body FROM template_pool") do |row|  | 
  
| 59 | 
      doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks}  | 
  
| 60 | 
              template_name = doc.root.at_css("NAME").content
     | 
  
| 61 | 
       | 
  
| 62 | 
              # Rename VMMMAD -> VM_MAD and TMMAD -> TM_MAD
     | 
  
| 63 | 
              boot = doc.root.at_xpath("TEMPLATE/OS/BOOT")
     | 
  
| 64 | 
              if boot.nil?
     | 
  
| 65 | 
                # No boot entry for template
     | 
  
| 66 | 
                next
     | 
  
| 67 | 
              end
     | 
  
| 68 | 
       | 
  
| 69 | 
              # Convert each boot device
     | 
  
| 70 | 
              # Skip template if:
     | 
  
| 71 | 
              # * the image referenced by a DISK is not found
     | 
  
| 72 | 
              # * a boot device in BOOT does not match any DISK
     | 
  
| 73 | 
      devs=[]  | 
  
| 74 | 
              nic_id=0
     | 
  
| 75 | 
       | 
  
| 76 | 
              begin
     | 
  
| 77 | 
      boot.content.split(",").each do |dev|  | 
  
| 78 | 
                  case dev.downcase
     | 
  
| 79 | 
       | 
  
| 80 | 
      when "hd", "cdrom"  | 
  
| 81 | 
      id = get_first_disk_id(dev, doc)  | 
  
| 82 | 
                      if id.nil?
     | 
  
| 83 | 
                        puts "Warning template #{row[:oid]} #{template_name}: skip nonexistent boot device '#{dev}'"
     | 
  
| 84 | 
                        next
     | 
  
| 85 | 
                      end
     | 
  
| 86 | 
                      devs.push("disk#{id}")
     | 
  
| 87 | 
       | 
  
| 88 | 
      when "network"  | 
  
| 89 | 
                      devs.push("nic#{nic_id}")
     | 
  
| 90 | 
                      nic_id += 1
     | 
  
| 91 | 
       | 
  
| 92 | 
      when /^(?:nic|disk)[0-9]+/  | 
  
| 93 | 
      devs.push(dev)  | 
  
| 94 | 
       | 
  
| 95 | 
                  end
     | 
  
| 96 | 
                end
     | 
  
| 97 | 
      rescue NoImageError => error  | 
  
| 98 | 
                puts "Skipping template #{row[:oid]} #{template_name}: #{error.message}"
     | 
  
| 99 | 
                next
     | 
  
| 100 | 
              end
     | 
  
| 101 | 
       | 
  
| 102 | 
      if boot.content != devs.join(",")  | 
  
| 103 | 
                # Avoid updating unchanged lines
     | 
  
| 104 | 
                new_boot = devs.join(",")
     | 
  
| 105 | 
       | 
  
| 106 | 
      if ops[:verbose]  | 
  
| 107 | 
                  puts "Template #{row[:oid]} #{template_name}: '#{boot.content}' -> '#{new_boot}'"
     | 
  
| 108 | 
                end
     | 
  
| 109 | 
       | 
  
| 110 | 
      boot.content = new_boot  | 
  
| 111 | 
       | 
  
| 112 | 
      @db[:template_pool].where(:oid => row[:oid]).update(  | 
  
| 113 | 
                  :body => doc.root.to_s)
     | 
  
| 114 | 
       | 
  
| 115 | 
              end
     | 
  
| 116 | 
            end
     | 
  
| 117 | 
       | 
  
| 118 | 
          end
     | 
  
| 119 | 
       | 
  
| 120 | 
      log_time()  | 
  
| 121 | 
       | 
  
| 122 | 
      return true  | 
  
| 123 | 
        end
     | 
  
| 124 | 
       | 
  
| 125 | 
        # Returns the ID of the first disk of a type
     | 
  
| 126 | 
        # Params:
     | 
  
| 127 | 
        # +type+:: type name of the disk, can be “hd” or “cdrom”
     | 
  
| 128 | 
        # +doc+:: Nokogiri::XML::Node describing the VM template
     | 
  
| 129 | 
      def get_first_disk_id(type, doc)  | 
  
| 130 | 
      doc.root.xpath("TEMPLATE/DISK").each_with_index do |disk, index|  | 
  
| 131 | 
            id = disk.at_css("IMAGE_ID")
     | 
  
| 132 | 
            if ! id.nil?
     | 
  
| 133 | 
      image = get_image_from_id(id.content)  | 
  
| 134 | 
            else
     | 
  
| 135 | 
      image = get_image_from_name(disk)  | 
  
| 136 | 
            end
     | 
  
| 137 | 
       | 
  
| 138 | 
      if is_image_type_matching?(image.at_css("TYPE").content, type)  | 
  
| 139 | 
              return index
     | 
  
| 140 | 
            end
     | 
  
| 141 | 
          end
     | 
  
| 142 | 
      return nil # Skip this type of device  | 
  
| 143 | 
        end
     | 
  
| 144 | 
       | 
  
| 145 | 
        # Returns a Nokogiri::XML::Node describing an image
     | 
  
| 146 | 
        # Params:
     | 
  
| 147 | 
        # +id+:: ID of the image
     | 
  
| 148 | 
      def get_image_from_id(id)  | 
  
| 149 | 
      @db.transaction do  | 
  
| 150 | 
      row = @db.fetch("SELECT body from image_pool where oid=#{id}").first  | 
  
| 151 | 
            # No image found, so unable to get image TYPE
     | 
  
| 152 | 
      raise NoImageError, "No image with ID '#{id}'" if row.nil?  | 
  
| 153 | 
      image = Nokogiri::XML(row[:body], nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks}  | 
  
| 154 | 
            return image
     | 
  
| 155 | 
          end
     | 
  
| 156 | 
        end
     | 
  
| 157 | 
       | 
  
| 158 | 
        # Returns a Nokogiri::XML::Node describing an image
     | 
  
| 159 | 
        # Params:
     | 
  
| 160 | 
        # +disk+:: Nokogiri::XML::Node describing a disk used by a template
     | 
  
| 161 | 
      def get_image_from_name(disk)  | 
  
| 162 | 
      name = disk.at_css("IMAGE").content # always defined  | 
  
| 163 | 
          uid = disk.at_css("IMAGE_UID")
     | 
  
| 164 | 
          uname = disk.at_css("IMAGE_UNAME")
     | 
  
| 165 | 
       | 
  
| 166 | 
      if ! name.nil? and (! uid.nil? or ! uname.nil?)  | 
  
| 167 | 
            if uid.nil?
     | 
  
| 168 | 
      uid = get_user_id(uname.content)  | 
  
| 169 | 
            else
     | 
  
| 170 | 
      uid = uid.content  | 
  
| 171 | 
            end
     | 
  
| 172 | 
       | 
  
| 173 | 
      @db.transaction do  | 
  
| 174 | 
      row = @db.fetch("SELECT body from image_pool where name=\"#{name}\" and uid=#{uid}").first  | 
  
| 175 | 
              # No image found, so unable to get image TYPE
     | 
  
| 176 | 
      raise NoImageError, "No image named '#{name}' for uid '#{uid}'" if row.nil?  | 
  
| 177 | 
      image = Nokogiri::XML(row[:body], nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks}  | 
  
| 178 | 
              return image
     | 
  
| 179 | 
            end
     | 
  
| 180 | 
          end
     | 
  
| 181 | 
        end
     | 
  
| 182 | 
       | 
  
| 183 | 
        # Returns the ID of a user name
     | 
  
| 184 | 
        # Params:
     | 
  
| 185 | 
        # +name+:: name of a user
     | 
  
| 186 | 
      def get_user_id(name)  | 
  
| 187 | 
      @db.transaction do  | 
  
| 188 | 
      row = @db.fetch("SELECT uid from user_pool where name=\"#{name}\"").first  | 
  
| 189 | 
      return row[:uid]  | 
  
| 190 | 
          end
     | 
  
| 191 | 
        end
     | 
  
| 192 | 
       | 
  
| 193 | 
        # Check if an image type match the type used in template BOOT
     | 
  
| 194 | 
        # Params:
     | 
  
| 195 | 
        # +image_type+:: numerical type of an image
     | 
  
| 196 | 
        # +wanted_type+:: string type extracted from VM template BOOT
     | 
  
| 197 | 
      def is_image_type_matching?(image_type, wanted_type)  | 
  
| 198 | 
          Integer(image_type) == IMAGE_TYPE.index(wanted_type.downcase)
     | 
  
| 199 | 
        end
     | 
  
| 200 | 
       | 
  
| 201 | 
      end
     |