diff --git a/src/onedb/database_schema.rb b/src/onedb/database_schema.rb index b39164f2fc..e98124f47e 100644 --- a/src/onedb/database_schema.rb +++ b/src/onedb/database_schema.rb @@ -132,7 +132,8 @@ class OneDBBacKEnd } } - LATEST_DB_VERSION = "6.0.0" + LATEST_DB_VERSION = '6.0.0' + LATEST_LOCAL_DB_VERSION = '6.0.0' def get_schema(type, version = nil) if !version diff --git a/src/onedb/fsck.rb b/src/onedb/fsck.rb index a48a9f981a..e68c70be38 100644 --- a/src/onedb/fsck.rb +++ b/src/onedb/fsck.rb @@ -30,6 +30,7 @@ require 'set' require 'nokogiri' require 'opennebula' +require 'database_schema' require 'fsck/pool_control' require 'fsck/user' @@ -52,8 +53,6 @@ require 'fsck/template' require 'fsck/quotas' module OneDBFsck - VERSION = "6.0.0" - LOCAL_VERSION = "6.0.0" def db_version if defined?(@db_version) && @db_version @@ -66,12 +65,12 @@ module OneDBFsck def check_db_version() # db_version = read_db_version() - if ( db_version[:version] != VERSION || - db_version[:local_version] != LOCAL_VERSION ) + if ( db_version[:version] != OneDBBacKEnd::LATEST_DB_VERSION || + db_version[:local_version] != OneDBBacKEnd::LATEST_LOCAL_DB_VERSION ) raise <<-EOT Version mismatch: fsck file is for version -Shared: #{VERSION}, Local: #{LOCAL_VERSION} +Shared: #{OneDBBacKEnd::LATEST_DB_VERSION}, Local: #{OneDBBacKEnd::LATEST_LOCAL_DB_VERSION} Current database is version Shared: #{db_version[:version]}, Local: #{db_version[:local_version]} @@ -80,7 +79,7 @@ EOT end def one_version - "OpenNebula #{VERSION}" + "OpenNebula #{OneDBBacKEnd::LATEST_DB_VERSION}" end # def db_version diff --git a/src/onedb/onedb b/src/onedb/onedb index ca824a0aae..4c31e44b3c 100755 --- a/src/onedb/onedb +++ b/src/onedb/onedb @@ -77,6 +77,13 @@ BACKUP={ :format => String } +NOBACKUP={ + :name => 'no_backup', + :large => '--no-backup', + :description => 'If used the backup file won\'t be automatically '\ + 'generated. Not recomended for production environments' +} + FEDERATED = { :name => 'federated', :large => '--federated', @@ -376,12 +383,23 @@ CommandParser::CmdParser.new(ARGV) do version_desc = <<-EOT.unindent Prints the current DB version. Use -v flag to see also OpenNebula version + + The version command will return different RC depending on the current + state of the installation: + + - 0: The current version of the DB match with the source version. + - 1: The database has not been bootstraped yet. + - 2: The DB version is older than required (upgrade needed). + - 3: The DB version is newer and not supported by this release. + - -1: Any other problem (e.g connection issues) EOT command :version, version_desc do begin helper = OneDB.new(options) helper.version(options) + rescue OneDBError => e + [e.code, e.message] rescue StandardError => e [-1, e.message] end @@ -433,7 +451,7 @@ CommandParser::CmdParser.new(ARGV) do command :upgrade, upgrade_desc, [:version, nil], - :options=>[FORCE, BACKUP] do + :options=>[FORCE, BACKUP, NOBACKUP] do begin helper = OneDB.new(options) helper.upgrade(args[0], options) diff --git a/src/onedb/onedb.rb b/src/onedb/onedb.rb index 09b9f48ae0..bd38f354de 100644 --- a/src/onedb/onedb.rb +++ b/src/onedb/onedb.rb @@ -203,33 +203,55 @@ class OneDB def version(ops) ret = @backend.read_db_version - if(ops[:verbose]) + if ops[:verbose] puts "Shared tables version: #{ret[:version]}" + puts "Required version: #{ret[:oned_version]}" - time = ret[:version] == "2.0" ? Time.now : Time.at(ret[:timestamp]) - puts "Timestamp: #{time.strftime("%m/%d %H:%M:%S")}" + time = Time.at(ret[:timestamp]) + puts "Timestamp: #{time.strftime('%m/%d %H:%M:%S')}" puts "Comment: #{ret[:comment]}" if ret[:local_version] puts puts "Local tables version: #{ret[:local_version]}" - + puts "Required version: #{ret[:oned_local_version]}" time = Time.at(ret[:local_timestamp]) - puts "Timestamp: #{time.strftime("%m/%d %H:%M:%S")}" + puts "Timestamp: #{time.strftime('%m/%d %H:%M:%S')}" puts "Comment: #{ret[:local_comment]}" if ret[:is_slave] puts - puts "This database is a federation slave" + puts 'This database is a federation slave' end end + puts else puts "Shared: #{ret[:version]}" puts "Local: #{ret[:local_version]}" + puts "Required shared version: #{ret[:oned_version]}" + puts "Required local version: #{ret[:oned_local_version]}" end - return 0 + need_update = update_available(ret) + if need_update[0] # Needs some update + case need_update[1] + when 2 # DB version < src version + puts + puts "Update required. Expected DB version is:\n"\ + "Local: #{ret[:oned_local_version]}\n"\ + "Shared: #{ret[:oned_version]}" + + return need_update[1] + when 3 # DB version > src version + puts + puts 'WARNING: Source version is lower than DB version' + + return need_update[1] + end + end + + 0 end def history @@ -242,17 +264,36 @@ class OneDB def upgrade(max_version, ops) one_not_running() - db_version = @backend.read_db_version + begin + db_version = @backend.read_db_version + rescue Exception => e + puts + puts e.message + puts e.backtrace.join("\n") + puts + puts + puts 'There was a failure retrieving the DB version.' + + return -2 + end + + if !update_available(db_version)[0] + puts 'Database schema is up to date.' + + return 0 + end if ops[:verbose] pretty_print_db_version(db_version) - puts "" + puts '' end - ops[:backup] = @backend.bck_file if ops[:backup].nil? + if !ops.include?(:no_backup) + ops[:backup] = @backend.bck_file if ops[:backup].nil? - backup(ops[:backup], ops) + backup(ops[:backup], ops) + end begin timea = Time.now @@ -309,7 +350,7 @@ class OneDB local = db_version[:local_version] shared = db_version[:version] - return 0 if local == OneDBBacKEnd::LATEST_DB_VERSION && + return 0 if local == OneDBBacKEnd::LATEST_LOCAL_DB_VERSION && shared == OneDBBacKEnd::LATEST_DB_VERSION STDERR.puts 'ERROR: Database upgrade to the latest versions ' \ @@ -333,13 +374,18 @@ class OneDB puts puts - puts "The database will be restored" + puts 'The database will be restored' ops[:force] = true - restore(ops[:backup], ops) + if !ops.include?(:no_backup) + restore(ops[:backup], ops) + else + puts 'WARNING: No backup has been restored as --no-backup'\ + ' flag was set. Note that DB state migh be inconsistent.' + end - return -1 + -1 end end @@ -703,4 +749,28 @@ class OneDB puts "This database is a federation slave" end end + + # db_version must be retrieved using corresponding function of @backend + def update_available(db_version) + local = db_version[:local_version] + shared = db_version[:version] + source = OneDBBacKEnd::LATEST_DB_VERSION + source_local = OneDBBacKEnd::LATEST_LOCAL_DB_VERSION + + shared_upgrade = Gem::Version.new(shared) < Gem::Version.new(source) + local_upgrade = Gem::Version.new(local) < Gem::Version.new(source_local) + + # Both need to be lower than current version as sometimes the DB schema + # have been increased only for local or shared. + return [true, 2] if shared_upgrade && local_upgrade + + shared_new = Gem::Version.new(shared) > Gem::Version.new(source) + local_new = Gem::Version.new(local) > Gem::Version.new(source_local) + + # Both version should be lower or equal than source. + return [true, 3] if shared_new || local_new + + [false, 0] + end + end diff --git a/src/onedb/onedb_backend.rb b/src/onedb/onedb_backend.rb index 6ab4e0f61d..6d1ad3885f 100644 --- a/src/onedb/onedb_backend.rb +++ b/src/onedb/onedb_backend.rb @@ -28,22 +28,42 @@ rescue LoadError exit -1 end +# ######################################################################### +# The Error Class represents a generic error in for OneDBBacKEnd. +# ######################################################################### +class OneDBError < StandardError + + attr_reader :code + + ERR_CODES = { + :db_bootstrap_err => 1, + :other_err => -1 + } + + def initialize(message, code) + @code=code + super(message) + end + +end + class OneDBBacKEnd - FEDERATED_TABLES = %w(group_pool user_pool acl zone_pool vdc_pool - marketplace_pool marketplaceapp_pool db_versioning) + + FEDERATED_TABLES = %w[group_pool user_pool acl zone_pool vdc_pool + marketplace_pool marketplaceapp_pool db_versioning] def read_db_version - connect_db - ret = {} begin - ret[:version] = "2.0" - ret[:timestamp] = 0 - ret[:comment] = "" + connect_db - @db.fetch("SELECT version, timestamp, comment FROM db_versioning " + - "WHERE oid=(SELECT MAX(oid) FROM db_versioning)") do |row| + ret[:version] = '2.0' + ret[:timestamp] = 0 + ret[:comment] = '' + + @db.fetch('SELECT version, timestamp, comment FROM db_versioning '\ + 'WHERE oid=(SELECT MAX(oid) FROM db_versioning)') do |row| ret[:version] = row[:version] ret[:timestamp] = row[:timestamp] ret[:comment] = row[:comment] @@ -54,46 +74,37 @@ class OneDBBacKEnd ret[:local_comment] = ret[:comment] ret[:is_slave] = false - begin - @db.fetch("SELECT version, timestamp, comment, is_slave FROM "+ - "local_db_versioning WHERE oid=(SELECT MAX(oid) "+ - "FROM local_db_versioning)") do |row| - ret[:local_version] = row[:version] - ret[:local_timestamp] = row[:timestamp] - ret[:local_comment] = row[:comment] - ret[:is_slave] = row[:is_slave] - end - rescue Exception => e - if e.class == Sequel::DatabaseConnectionError - raise e - end + @db.fetch('SELECT version, timestamp, comment, is_slave FROM '\ + 'local_db_versioning WHERE oid=(SELECT MAX(oid) '\ + 'FROM local_db_versioning)') do |row| + ret[:local_version] = row[:version] + ret[:local_timestamp] = row[:timestamp] + ret[:local_comment] = row[:comment] + ret[:is_slave] = row[:is_slave] end - return ret + ret[:oned_version] = OneDBBacKEnd::LATEST_DB_VERSION + ret[:oned_local_version] = OneDBBacKEnd::LATEST_LOCAL_DB_VERSION + ret + # rubocop:disable Lint/RescueException rescue Exception => e if e.class == Sequel::DatabaseConnectionError - raise e - elsif !db_exists? - # If the DB doesn't have db_version table, it means it is empty or a 2.x - raise "Database schema does not look to be created by " << - "OpenNebula: table user_pool is missing or empty, " << - "oneadmin user must always exist." + raise OneDBError.new(e.message, + OneDBError::ERR_CODES[:connection_err]) end - begin - # Table image_pool is present only in 2.X DBs - @db.fetch("SELECT * FROM image_pool") { |row| } - rescue - raise "Database schema looks to be created by OpenNebula 1.X." << - "This tool only works with databases created by 2.X versions." + if !db_exists? + # If the DB doesn't have db_version table, it means it is empty + msg = 'Database is not correctly bootstraped.' + + raise OneDBError.new(msg, + OneDBError::ERR_CODES[:db_bootstrap_err]) end - comment = "Could not read any previous db_versioning data, " << - "assuming it is an OpenNebula 2.0 or 2.2 DB." - - return ret + ret end + # rubocop:enable Lint/RescueException end def history