class Replicator
Check differences between files and copy them
Constants
- FED_ATTRS
- FILES
- FOLDERS
- ONE_AUTH
- SSH_OPTIONS
Public Class Methods
Class constructor
@param ssh_key [String] SSH key file path @param server [String] OpenNebula server IP address
# File lib/one_helper/onezone_helper.rb, line 67 def initialize(ssh_key, server) @oneadmin_identity_file = ssh_key @remote_server = server # Get local configuration l_credentials = File.read(ONE_AUTH).gsub("\n", '') l_endpoint = 'http://localhost:2633/RPC2' local_client = Client.new(l_credentials, l_endpoint) @l_config = OpenNebula::System.new(local_client).get_configuration @l_config_elements = { :raw => @l_config } @l_fed_elements = { :raw => @l_config } if OpenNebula.is_error?(@l_config) STDERR.puts 'Unable to read OpenNebula configuration. ' \ 'Is OpenNebula running?' exit(-1) end fetch_db_config(@l_config_elements) fetch_fed_config(@l_fed_elements) # Get remote configuration r_credentials = ssh("cat #{ONE_AUTH}").stdout.gsub("\n", '') r_endpoint = "http://#{server}:2633/RPC2" remote_client = Client.new(r_credentials, r_endpoint) @r_config = OpenNebula::System.new(remote_client).get_configuration @r_config_elements = { :raw => @r_config } @r_fed_elements = { :raw => @r_config } fetch_db_config(@r_config_elements) fetch_fed_config(@r_fed_elements) # Set OpenNebula services to not restart @opennebula_services = { 'opennebula' => false, 'opennebula-sunstone' => false, 'opennebula-gate' => false, 'opennebula-flow' => false } end
Public Instance Methods
Process files and folders
@param sync_database [Boolean] True to sync database
# File lib/one_helper/onezone_helper.rb, line 111 def process_files(sync_database) # Files to be copied copy_onedconf FILES.each do |file| copy_and_check(file[:name], file[:service]) end # Folders to be copied FOLDERS.each do |folder| copy_folder(folder[:name], folder[:service]) end restart_services # Sync database sync_db if sync_database end
Private Instance Methods
Replaces a file with the version located on a remote server Only replaces the file if it's different from the remote one
@param file [String] File to check @param service [String] Service to restart
# File lib/one_helper/onezone_helper.rb, line 166 def copy_and_check(file, service) puts "Checking #{file}" temp_file = Tempfile.new("#{file}-temp") scp("/etc/one/#{file}", temp_file.path) if !FileUtils.compare_file(temp_file, "/etc/one/#{file}") FileUtils.cp(temp_file.path, "/etc/one/#{file}") puts "#{file} has been replaced by #{@remote_server}:#{file}" @opennebula_services[service] = true end ensure temp_file.unlink end
Copy folders
@param folder [String] Folder to copy @param service [String] Service to restart
# File lib/one_helper/onezone_helper.rb, line 188 def copy_folder(folder, service) puts "Checking #{folder}" rc = run_command( "rsync -ai\ -e \"ssh #{SSH_OPTIONS} -i #{@oneadmin_identity_file}\" " \ "#{@remote_server}:/etc/one/#{folder}/ " \ "/etc/one/#{folder}/" ) unless rc rc = run_command( "rsync -ai\ -e \"ssh #{SSH_OPTIONS} -i #{@oneadmin_identity_file}\" " \ "oneadmin@#{@remote_server}:/etc/one/#{folder}/ " \ "/etc/one/#{folder}/" ) end unless rc STDERR.puts 'ERROR' STDERR.puts "Fail to sync #{folder}" exit(-1) end output = rc.stdout return if output.empty? puts "Folder #{folder} has been sync with #{@remote_server}:#{folder}" @opennebula_services[service] = true end
oned.conf file on distributed environments will always be different, due to the federation section. Replace oned.conf based on a remote server's version maintaining the old FEDERATION section
# File lib/one_helper/onezone_helper.rb, line 226 def copy_onedconf puts 'Checking oned.conf' # Create temporarhy files l_oned = Tempfile.new('l_oned') r_oned = Tempfile.new('r_oned') l_oned.close r_oned.close # Copy remote and local oned.conf files to temporary files scp('/etc/one/oned.conf', r_oned.path) FileUtils.cp('/etc/one/oned.conf', l_oned.path) # Create augeas objects to manage oned.conf files l_work_file_dir = File.dirname(l_oned.path) l_work_file_name = File.basename(l_oned.path) r_work_file_dir = File.dirname(r_oned.path) r_work_file_name = File.basename(r_oned.path) l_aug = Augeas.create(:no_modl_autoload => true, :no_load => true, :root => l_work_file_dir, :loadpath => l_oned.path) l_aug.clear_transforms l_aug.transform(:lens => 'Oned.lns', :incl => l_work_file_name) l_aug.context = "/files/#{l_work_file_name}" l_aug.load r_aug = Augeas.create(:no_modl_autoload => true, :no_load => true, :root => r_work_file_dir, :loadpath => r_oned.path) r_aug.clear_transforms r_aug.transform(:lens => 'Oned.lns', :incl => r_work_file_name) r_aug.context = "/files/#{r_work_file_name}" r_aug.load # Get local federation information fed_attrs = [] FED_ATTRS.each do |attr| fed_attrs << l_aug.get("FEDERATION/#{attr}") end # Remove federation section l_aug.rm('FEDERATION') r_aug.rm('FEDERATION') # Save augeas files in temporary directories l_aug.save r_aug.save return if FileUtils.compare_file(l_oned.path, r_oned.path) time_based_identifier = Time.now.to_i # backup oned.conf FileUtils.cp('/etc/one/oned.conf', "/etc/one/oned.conf#{time_based_identifier}") FED_ATTRS.zip(fed_attrs) do |name, value| r_aug.set("FEDERATION/#{name}", value) end r_aug.save FileUtils.cp(r_oned.path, '/etc/one/oned.conf') puts 'oned.conf has been replaced by ' \ "#{@remote_server}:/etc/one/oned.conf" puts 'A copy of your old oned.conf file is located here: ' \ "/etc/one/oned.conf#{time_based_identifier}" @opennebula_services['opennebula'] = true end
Get database configuration
@param configs [Object] Configuration
# File lib/one_helper/onezone_helper.rb, line 135 def fetch_db_config(configs) configs.store(:backend, configs[:raw]['/OPENNEBULA_CONFIGURATION/DB/BACKEND']) if configs[:backend] == 'mysql' || configs[:backend] == 'postgresql' configs.store(:server, configs[:raw]['/OPENNEBULA_CONFIGURATION/DB/SERVER']) configs.store(:user, configs[:raw]['/OPENNEBULA_CONFIGURATION/DB/USER']) configs.store(:password, configs[:raw]['/OPENNEBULA_CONFIGURATION/DB/PASSWD']) configs.store(:dbname, configs[:raw]['/OPENNEBULA_CONFIGURATION/DB/DB_NAME']) configs.store(:port, configs[:raw]['/OPENNEBULA_CONFIGURATION/DB/PORT']) configs[:port] = '3306' if configs[:port] == '0' else STDERR.puts 'No mysql or postgresql backend configuration found' exit(-1) end end
Get federation configuration
@param configs [Object] Configuration
# File lib/one_helper/onezone_helper.rb, line 154 def fetch_fed_config(configs) configs.store(:server_id, configs[:raw]['/OPENNEBULA_CONFIGURATION/FEDERATION/SERVER_ID']) configs.store(:zone_id, configs[:raw]['/OPENNEBULA_CONFIGURATION/FEDERATION/ZONE_ID']) end
Restart OpenNebula services
# File lib/one_helper/onezone_helper.rb, line 419 def restart_services restarted = false @opennebula_services.each do |service, status| next unless status service_action(service) restarted = true end return if restarted puts 'Everything seems synchronized, nothing was replaced.' end
Run local command
@param cmd [String] Command to run @param print_output [Boolean] True to show output
# File lib/one_helper/onezone_helper.rb, line 347 def run_command(cmd, print_output = false) output = LocalCommand.run(cmd) if output.code == 0 output else return false unless print_output STDERR.puts 'ERROR' STDERR.puts "Failed to run: #{cmd}" STDERR.puts output.stderr false end end
Execute SCP command
@param src [String] Source path @param dest [String] Destination path
# File lib/one_helper/onezone_helper.rb, line 396 def scp(src, dest) rc = run_command( "scp -i #{@oneadmin_identity_file} " \ "#{SSH_OPTIONS} #{@remote_server}:/#{src} #{dest}" ) # if default users doesn't work, try with oneadmin unless rc rc = run_command( "scp -i #{@oneadmin_identity_file} " \ "#{SSH_OPTIONS} oneadmin@#{@remote_server}:#{src} #{dest}" ) end # if oneadmin doesn't work neither, fail unless rc STDERR.puts 'ERROR' STDERR.puts "Couldn't execute command #{cmd} on remote host" exit(-1) end end
Service action
@param service [String] Service to restart @param action [String] Action to execute (start, stop, restart)
# File lib/one_helper/onezone_helper.rb, line 439 def service_action(service, action = 'try-restart') if `file /sbin/init`.include? 'systemd' puts "#{action}ing #{service} via systemd" run_command("systemctl #{action} #{service}", true) else puts "#{action}ing #{service} via init" run_command("service #{service} #{action}", true) end end
Execute SSH command
@param cmd [String] Command to execute
# File lib/one_helper/onezone_helper.rb, line 366 def ssh(cmd) rc = run_command( "ssh -i #{@oneadmin_identity_file} " \ "#{SSH_OPTIONS} #{@remote_server} " \ "#{cmd}" ) # if default users doesn't work, try with oneadmin unless rc rc = run_command( "ssh -i #{@oneadmin_identity_file} " \ "#{SSH_OPTIONS} oneadmin@#{@remote_server} " \ "#{cmd}" ) end # if oneadmin doesn't work neither, fail unless rc STDERR.puts 'ERROR' STDERR.puts "Couldn't execute command #{cmd} on remote host" exit(-1) end rc end
Sync database
# File lib/one_helper/onezone_helper.rb, line 309 def sync_db puts "Dumping and fetching database from #{@remote_server}, " \ 'this could take a while' ssh( "onedb backup -f -t #{@r_config_elements[:backend]} " \ "-u #{@r_config_elements[:user]} " \ "-p #{@r_config_elements[:password]} " \ "-d #{@r_config_elements[:dbname]} " \ "-P #{@r_config_elements[:port]} /tmp/one_db_dump.sql" ) scp('/tmp/one_db_dump.sql', '/tmp/one_db_dump.sql') puts "Local OpenNebula's database will be replaced, hang tight" service_action('opennebula', 'stop') puts 'Restoring database' run_command( "onedb restore -f -t #{@l_config_elements[:backend]}" \ "-u #{@l_config_elements[:user]} " \ "-p #{@l_config_elements[:password]} " \ "-d #{@l_config_elements[:dbname]} " \ "-P #{@l_config_elements[:port]} " \ "-h #{@l_config_elements[:server]}" \ '/tmp/one_db_dump.sql', true ) service_action('opennebula', 'start') end