From fc3ab2d7ab4d6f39ec91c22b3aa202039e840d21 Mon Sep 17 00:00:00 2001 From: Jaime Melis Date: Wed, 13 Apr 2016 15:51:15 +0200 Subject: [PATCH] Migrate VN_MAD from the Host to the Network --- install.sh | 3 +- src/onedb/local/4.13.85_to_4.90.0.rb | 207 +++++++++++++++++++++++++++ src/onedb/patches/vnmad.rb | 194 +++++++++++++++++++++++++ 3 files changed, 403 insertions(+), 1 deletion(-) create mode 100644 src/onedb/patches/vnmad.rb diff --git a/install.sh b/install.sh index fd161e70e9..77c285f687 100755 --- a/install.sh +++ b/install.sh @@ -1217,7 +1217,8 @@ ONEDB_LOCAL_MIGRATOR_FILES="src/onedb/local/4.5.80_to_4.7.80.rb \ src/onedb/local/4.13.85_to_4.90.0.rb" ONEDB_PATCH_FILES="src/onedb/patches/4.14_monitoring.rb \ - src/onedb/patches/history_times.rb" + src/onedb/patches/history_times.rb \ + src/onedb/patches/vnmad.rb" #------------------------------------------------------------------------------- # Configuration files for OpenNebula, to be installed under $ETC_LOCATION diff --git a/src/onedb/local/4.13.85_to_4.90.0.rb b/src/onedb/local/4.13.85_to_4.90.0.rb index 6ec236d25c..50559f6bbe 100644 --- a/src/onedb/local/4.13.85_to_4.90.0.rb +++ b/src/onedb/local/4.13.85_to_4.90.0.rb @@ -14,6 +14,8 @@ # limitations under the License. # #--------------------------------------------------------------------------- # +require 'set' + require 'opennebula' include OpenNebula @@ -471,6 +473,211 @@ module Migrator log_time() + # Move HOST/VN_MAD --> VNET/VN_MAD + + log_time() + + # Build net_vnmad + net_vnmad = {} + @db.transaction do + @db.fetch("SELECT * FROM vm_pool") do |row| + doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks} + state = row[:state].to_i + + if state != 6 + vnmads = Set.new + doc.root.xpath("HISTORY_RECORDS/HISTORY/VNMMAD").collect{|v| vnmads << v.text } + + doc.root.xpath("TEMPLATE/NIC/NETWORK_ID").each do |net_id| + net_id = net_id.text.to_i + + net_vnmad[net_id] ||= Set.new + net_vnmad[net_id] += vnmads + end + end + end + end + + # Build cluster_vnmad and fix hosts + @db.run "ALTER TABLE host_pool RENAME TO old_host_pool;" + @db.run "CREATE TABLE host_pool (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, state INTEGER, last_mon_time INTEGER, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER, cid INTEGER);" + + cluster_vnmad = {} + @db.transaction do + @db.fetch("SELECT * FROM old_host_pool") do |row| + doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks} + + # Get cluster + cluster_id = doc.root.xpath('CLUSTER_ID').text.to_i + + # Store VN_MAD + vnmad = doc.root.xpath('VN_MAD').text + + cluster_vnmad[cluster_id] ||= Set.new + cluster_vnmad[cluster_id] << vnmad + + # Remove VN_MAD + doc.root.xpath('//VN_MAD').remove + + row[:body] = doc.root.to_s + @db[:host_pool].insert(row) + end + end + @db.run "DROP TABLE old_host_pool;" + + # Fix Networks + + # So far we have two hashes: net_vnmad, which lists the specific vnmad found + # for each network, based on the nics of the VMs, and cluster_vnmad, with + # the vnmad found in the hosts. + # + # We will report a warning if either the cluster or the network has more + # than one vnmad. We will automatically choose one vnmad from the network + # list, or if empty from the cluster list. + # + # That vnmad may be changed through onedb patch. + # + # It could happen that no network is found in the net or in the cluster, in + # which case the admin **must** run the onedb patch, it's not optional any + # more. + + @db.run "ALTER TABLE network_pool RENAME TO old_network_pool;" + @db.run "CREATE TABLE network_pool (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER, pid INTEGER, UNIQUE(name,uid));" + + suggest_patch = false + final_net_vnmad = {} + @db.transaction do + @db.fetch("SELECT * FROM old_network_pool") do |row| + doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks} + + net_id = row[:oid] + cluster_id = doc.root.xpath('CLUSTERS/ID').first.text.to_i + + net_vnmad_len = net_vnmad[net_id].length rescue 0 + cluster_vnmad_len = cluster_vnmad[cluster_id].length rescue 0 + + if (vlan = doc.root.xpath('VLAN')) + vlan = vlan.text + if !vlan.empty? && vlan.to_i == 0 + dummy_net = true + end + end + + doc.root.xpath('//VLAN').remove + + if dummy_net + vnmad = "fw" + else + vnmad = nil + other_vnmads = nil + + # net_vnmad_len == 1 => that one + # net_vnmad_len > 1 => get first one and show warning + # net_vnmad_len == 0 && cluster_vnmad_len == 1 => that one + # net_vnmad_len == 0 && cluster_vnmad_len > 1 => first one and show warning + # net_vnmad_len == 0 && cluster_vnmad_len == 0 => force onedb patch + + if net_vnmad_len == 1 + vnmad = net_vnmad[net_id].first + elsif net_vnmad_len > 1 + vnmad = net_vnmad[net_id].first + other_vnmads = net_vnmad[net_id] - Set[vnmad] + elsif net_vnmad_len == 0 && cluster_vnmad_len == 1 + vnmad = cluster_vnmad[cluster_id].first + elsif net_vnmad_len == 0 && cluster_vnmad_len > 1 + vnmad = cluster_vnmad[cluster_id].first + other_vnmads = cluster_vnmad[cluster_id] - Set[vnmad] + end + + if vnmad && other_vnmads + if !suggest_patch + puts + puts "**************************************************************" + puts "* WARNING WARNING WARNING WARNING WARNING WARNING WARNING *" + puts "**************************************************************" + end + + suggest_patch = true + puts "* Net ##{net_id} assigned VN_MAD=#{vnmad}. " << + "Other options: #{other_vnmads.to_a.join(', ')}" + end + end + + final_net_vnmad[net_id] = vnmad + + if vnmad + doc.root.add_child(doc.create_element("VN_MAD")).content = vnmad + end + + row[:body] = doc.root.to_s + @db[:network_pool].insert(row) + end + end + + patch_cmd = "onedb patch [opts] /usr/lib/one/ruby/onedb/patches/vnmad.rb" + if suggest_patch + puts + puts "If you want to change the above assignments, please run:" + puts patch_cmd + end + + net_unassigned = final_net_vnmad.select{|k,v| v.nil?}.keys + if !net_unassigned.empty? + puts + puts "**************************************************************" + puts "* ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR *" + puts "**************************************************************" + puts + puts "You **must** run:" + puts patch_cmd + puts + puts "to assign a vnmad to the following networks:" + net_unassigned.each{|n| puts " - #{n}"} + puts + end + + @db.run "DROP TABLE old_network_pool;" + + # Fix VMs + + @db.run "ALTER TABLE vm_pool RENAME TO old_vm_pool;" + @db.run "CREATE TABLE IF NOT EXISTS vm_pool (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, uid INTEGER, gid INTEGER, last_poll INTEGER, state INTEGER, lcm_state INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER)" + + @db.transaction do + @db.fetch("SELECT * FROM old_vm_pool") do |row|2 + doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks} + state = row[:state].to_i + + if state != 6 + # Remove vnmad from the history records + doc.root.xpath("HISTORY_RECORDS//VNMMAD").remove + doc.root.xpath("HISTORY_RECORDS//DS_LOCATION").remove + + # Rename VMMMAD -> VM_MAD and TMMAD -> TM_MAD + doc.root.xpath("HISTORY_RECORDS//VMMMAD").each {|e| e.name = "VM_MAD"} + doc.root.xpath("HISTORY_RECORDS//TMMAD").each {|e| e.name = "TM_MAD"} + + # Add vnmad to the nics + doc.root.xpath('TEMPLATE/NIC').each do |nic| + net_id = nic.xpath("NETWORK_ID").text.to_i rescue nil # NICs without network may exist + + next unless net_id + + vnmad = final_net_vnmad[net_id] + + if vnmad + nic.add_child(doc.create_element("VN_MAD")).content = vnmad + end + end + + row[:body] = doc.root.to_s + end + + @db[:vm_pool].insert(row) + end + end + @db.run "DROP TABLE old_vm_pool;" + return true end diff --git a/src/onedb/patches/vnmad.rb b/src/onedb/patches/vnmad.rb new file mode 100644 index 0000000000..6d5ebe7cde --- /dev/null +++ b/src/onedb/patches/vnmad.rb @@ -0,0 +1,194 @@ +# -------------------------------------------------------------------------- # +# 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.90.0" + LOCAL_VERSION = "4.90.0" + + 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 + + def patch(ops) + init_log_time() + + all_nets = {} + fix_nets = {} + + @db.transaction do + @db.fetch("SELECT * FROM network_pool") do |row| + doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks} + vnmad = doc.root.xpath("VN_MAD").text rescue "" + all_nets[row[:oid]] = {:name => row[:name], :vnmad => vnmad} + + if vnmad.empty? + fix_nets[row[:oid]] = "" + end + end + end + + if !fix_nets.empty? + puts "You need to input a VN_MAD for the following networks:" + fix_nets.each do |net_id, vnmad| + input = "" + while ( input.empty? ) do + name = all_nets[net_id][:name] + puts "##{net_id} (#{name}) => VN_MAD:" + input = STDIN.gets.chomp.strip + end + fix_nets[net_id] = input + end + end + + puts + puts "Change additional networks:" + + input = "" + while (input != "c") + puts + puts "Input the ID of the network to change or 'c' to continue" + + input = STDIN.gets.chomp.strip + + if input.match(/^\d+$/) + net_id = input.to_i + + if all_nets[net_id].nil? + puts "Error: Net ##{net_id} not found." + next + end + + input_vnmad = "" + while ( input_vnmad.empty? ) do + name = all_nets[net_id][:name] + current_vnmad = all_nets[net_id][:vnmad] + puts "##{net_id} (#{name}) (current VN_MAD='#{current_vnmad}') => VN_MAD:" + input_vnmad = STDIN.gets.chomp.strip + end + fix_nets[net_id] = input_vnmad + end + end + + puts "Review these assignemnts:" + + fix_nets.keys.sort.each do |net_id| + name = all_nets[net_id][:name] + vnmad = fix_nets[net_id] + + current_vnmad = all_nets[net_id][:vnmad] + + msg = "* ##{net_id} (#{name}) => VN_MAD='#{vnmad}'" + + if !current_vnmad.empty? + msg << " (current VN_MAD='#{current_vnmad}')" + end + + puts msg + end + + puts + + input = "" + while ( !input.match(/^(y|n)$/)) do + puts "Do you wish to apply the above? (y/n)" + input = STDIN.gets.chomp.strip.downcase + end + + if input == "n" + puts "Quitting without changing anything." + raise "Execution canceled by user" + end + + @db.run "ALTER TABLE network_pool RENAME TO old_network_pool;" + @db.run "CREATE TABLE network_pool (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, uid INTEGER, gid INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER, pid INTEGER, UNIQUE(name,uid));" + + @db.transaction do + @db.fetch("SELECT * FROM old_network_pool") do |row| + doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks} + + net_id = row[:oid] + vnmad = fix_nets[net_id] + + if vnmad + doc.root.add_child(doc.create_element("VN_MAD")).content = vnmad + end + + row[:body] = doc.root.to_s + @db[:network_pool].insert(row) + end + end + + @db.run "DROP TABLE old_network_pool;" + + @db.run "ALTER TABLE vm_pool RENAME TO old_vm_pool;" + @db.run "CREATE TABLE IF NOT EXISTS vm_pool (oid INTEGER PRIMARY KEY, name VARCHAR(128), body MEDIUMTEXT, uid INTEGER, gid INTEGER, last_poll INTEGER, state INTEGER, lcm_state INTEGER, owner_u INTEGER, group_u INTEGER, other_u INTEGER)" + + @db.transaction do + @db.fetch("SELECT * FROM old_vm_pool") do |row| + doc = Nokogiri::XML(row[:body],nil,NOKOGIRI_ENCODING){|c| c.default_xml.noblanks} + state = row[:state].to_i + + if state != 6 + doc.root.xpath('TEMPLATE/NIC').each do |nic| + net_id = nic.xpath("NETWORK_ID").text.to_i rescue nil # NICs without network may exist + next unless net_id + + vnmad = fix_nets[net_id] + + if vnmad + nic.add_child(doc.create_element("VN_MAD")).content = vnmad + end + end + + row[:body] = doc.root.to_s + end + + @db[:vm_pool].insert(row) + end + end + @db.run "DROP TABLE old_vm_pool;" + + log_time() + + return true + end +end