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
|