1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-01-24 02:03:52 +03:00

Bug #4000: Fix history etime with onedb

(cherry picked from commit ee44b97013eee45e0a1ace90520832f93292e73e)
This commit is contained in:
Carlos Martín 2015-10-08 16:14:27 +02:00
parent 1643d66f39
commit f3ba7ee1db
3 changed files with 238 additions and 1 deletions

View File

@ -1246,7 +1246,8 @@ ONEDB_LOCAL_MIGRATOR_FILES="src/onedb/local/4.5.80_to_4.7.80.rb \
src/onedb/local/4.11.80_to_4.13.80.rb \
src/onedb/local/4.13.80_to_4.13.85.rb"
ONEDB_PATCH_FILES="src/onedb/patches/4.14_monitoring.rb"
ONEDB_PATCH_FILES="src/onedb/patches/4.14_monitoring.rb \
src/onedb/patches/history_times.rb"
#-------------------------------------------------------------------------------
# Configuration files for OpenNebula, to be installed under $ETC_LOCATION

View File

@ -919,6 +919,62 @@ EOT
log_time()
# Bug #4000 may cause history records with etime=0 when they should
# be closed. The last history can be fixed with the VM etime, but
# previous history entries need to be fixed manually
# Query to select history elements that:
# - have etime = 0
# - are not the last seq
@db.fetch("SELECT vid,seq FROM history WHERE (etime = 0 AND seq <> (SELECT MAX(seq) FROM history AS subhistory WHERE history.vid = subhistory.vid) )") do |row|
log_error("History record for VM #{row[:vid]} seq # #{row[:seq]} is not closed (etime = 0)", false)
# Aqui no hace falta modificar la VM, solo el history etime
end
log_time()
history_fix = []
# Query to select history elements that have:
# - etime = 0
# - is last seq
# - VM is DONE
@db.fetch("SELECT * FROM history WHERE (etime = 0 AND vid IN (SELECT oid FROM vm_pool WHERE state=6) AND seq = (SELECT MAX(seq) FROM history AS subhistory WHERE history.vid=subhistory.vid))") do |row|
log_error("History record for VM #{row[:vid]} seq # #{row[:seq]} is not closed (etime = 0), but the VM is in state DONE")
etime = 0
@db.fetch("SELECT body FROM vm_pool WHERE oid=#{row[:vid]}") do |vm_row|
vm_doc = Nokogiri::XML(vm_row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks}
etime = vm_doc.root.at_xpath("ETIME").text.to_i
end
history_doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks}
["RETIME", "ESTIME", "EETIME", "ETIME"].each do |att|
elem = history_doc.root.at_xpath(att)
if (elem.text == "0")
elem.content = etime
end
end
row[:body] = history_doc.root.to_s
row[:etime] = etime
history_fix.push(row)
end
@db.transaction do
history_fix.each do |row|
@db[:history].where({:vid => row[:vid], :seq => row[:seq]}).update(row)
end
end
log_time()
########################################################################
# Hosts
#

View File

@ -0,0 +1,180 @@
# -------------------------------------------------------------------------- #
# Copyright 2002-2015, OpenNebula Project, OpenNebula Systems #
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
# not use this file except in compliance with the License. You may obtain #
# a copy of the License at #
# #
# http://www.apache.org/licenses/LICENSE-2.0 #
# #
# Unless required by applicable law or agreed to in writing, software #
# distributed under the License is distributed on an "AS IS" BASIS, #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
# See the License for the specific language governing permissions and #
# limitations under the License. #
#--------------------------------------------------------------------------- #
if !ONE_LOCATION
LOG_LOCATION = "/var/log/one"
else
LOG_LOCATION = ONE_LOCATION + "/var"
end
LOG = LOG_LOCATION + "/onedb-fsck.log"
require 'nokogiri'
module OneDBPatch
VERSION = "4.11.80"
LOCAL_VERSION = "4.13.85"
def is_hot_patch(ops)
return false
end
def check_db_version(ops)
db_version = read_db_version()
if ( db_version[:version] != VERSION ||
db_version[:local_version] != LOCAL_VERSION )
raise <<-EOT
Version mismatch: patch file is for version
Shared: #{VERSION}, Local: #{LOCAL_VERSION}
Current database is version
Shared: #{db_version[:version]}, Local: #{db_version[:local_version]}
EOT
end
end
ATTRIBUTES = [
["STIME", "Start time"],
["PSTIME", "Prolog start time"],
["PETIME", "Prolog end time"],
["RSTIME", "Running start time"],
["RETIME", "Running end time"],
["ESTIME", "Epilog start time"],
["EETIME", "Epilog end time"],
["ETIME", "End time"]
]
def patch(ops)
init_log_time()
puts "This tool will allow you to edit the timestamps of VM history records, used to calculate accounting and showback."
input = ""
while ( input.to_i.to_s != input ) do
print "VM ID: "
input = STDIN.gets.chomp.strip
end
vid = input.to_i
input = ""
while ( input.to_i.to_s != input ) do
print "History sequence number: "
input = STDIN.gets.chomp.strip
end
seq = input.to_i
puts
history_row = nil
history_doc = nil
@db.fetch("SELECT * FROM history WHERE vid = #{vid} AND seq = #{seq}") do |row|
history_doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks}
pphistory(history_doc)
puts
puts "To set new values:\n empty to use current value; <YYYY-MM-DD HH:MM:SS> in UTC; or 0 to leave unset (open history record)."
ATTRIBUTES.each do |att, desc|
elem = history_doc.root.at_xpath(att)
puts "%-6s %-20s: %-25s" % [att, desc, pptime(elem.text)]
elem.content = parsetime(elem.text)
puts
end
row[:body] = history_doc.root.to_s
row[:etime] = history_doc.root.at_xpath("ETIME").text.to_i
history_row = row
end
if (history_row)
puts
puts "The history record # #{history_row[:seq]} for VM #{history_row[:vid]} will be updated with these new values:"
pphistory(history_doc)
puts
print "Confirm to write to the database [Y/n]: "
input = STDIN.gets.chomp.strip.downcase
if !(input == "" || input == "y")
raise "Execution canceled by user"
end
@db.transaction do
@db[:history].where({:vid => history_row[:vid], :seq => history_row[:seq]}).update(history_row)
end
else
puts "History record # #{seq} for VM #{vid} does not exist in the database."
end
log_time()
return true
end
def pphistory(history_doc)
ATTRIBUTES.each do |att, desc|
elem = history_doc.root.at_xpath(att)
puts "%-6s %-20s: %-25s" % [att, desc, pptime(elem.text)]
end
end
# Pretty print time
def pptime(t)
if (t.to_i == 0)
return "0 (unset)"
end
return Time.at(t.to_i).utc.strftime("%F %T %Z")
end
def parsetime(t)
print "New value : "
input = STDIN.gets.chomp.strip
while true
if (input == "")
return t.to_i
end
if (input == "0")
return 0
end
st = input.gsub(" ", "T")
st << ("Z")
begin
return Time.iso8601(st).to_i
rescue ArgumentError => e
puts "Argument is not valid. Time must be in UTC timezone, and formatted as <YYYY-MM-DD HH:MM:SS>. Failure: #{e.to_s}"
print "New value : "
input = STDIN.gets.chomp.strip
end
end
end
end