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
|