diff options
Diffstat (limited to 'lib/tasks/gs_cluster.rake')
-rw-r--r-- | lib/tasks/gs_cluster.rake | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/lib/tasks/gs_cluster.rake b/lib/tasks/gs_cluster.rake new file mode 100644 index 0000000..565fd83 --- /dev/null +++ b/lib/tasks/gs_cluster.rake @@ -0,0 +1,333 @@ +namespace :gs_cluster do + desc "Sync local data to other gs cluster nodes." + task :push_waiting_data_to_other_nodes => :environment do + infinity_loop_protection_counter = GsClusterSyncLogEntry.where(:homebase_ip_address => HOMEBASE_IP_ADDRESS, + :waiting_to_be_synced => true).count + 10 + + # One bite at a time. + # + while GsClusterSyncLogEntry.where(:homebase_ip_address => HOMEBASE_IP_ADDRESS, + :waiting_to_be_synced => true).any? && + infinity_loop_protection_counter > 0 + GsClusterSyncLogEntry.where(:homebase_ip_address => HOMEBASE_IP_ADDRESS, + :waiting_to_be_synced => true).first.populate_other_cluster_nodes + infinity_loop_protection_counter -= 1 + end + + end + + desc "Reset gs_cluster_sync_log." + task :reset_sync_log => :environment do + GsClusterSyncLogEntry.destroy_all + + User.where('is_native IS NOT FALSE').each do |user| + puts("Processing User=#{user.id}/#{user.uuid} - #{user.user_name}") + user.create_on_other_gs_nodes + end + + SipAccount.where('is_native IS NOT FALSE').each do |sip_account| + puts("Processing SipAccount=#{sip_account.id}/#{sip_account.uuid} - #{sip_account.auth_name}"); + sip_account.create_on_other_gs_nodes + end + + PhoneNumber.where('is_native IS NOT FALSE AND phone_numberable_type IN ("SipAccount", "Conference", "FaxAccount", "Callthrough", "HuntGroup", "AutomaticCallDistributor")').each do |phone_number| + puts("Processing PhoneNumber=#{phone_number.id}/#{phone_number.uuid} - #{phone_number.number}"); + phone_number.create_on_other_gs_nodes + end + end + + desc "Pull objects from nodes." + task :pull => :environment do + local_node = GsNode.where(:ip_address => HOMEBASE_IP_ADDRESS).first + GsNode.where(:accepts_updates_from => true).each do |remote_node| + if remote_node.id == local_node.id + next + end + + puts "Processing node: #{remote_node.name}" + pull_node(remote_node, local_node) + end + end + + def pull_node(remote_node, local_node) + require 'nokogiri' + require 'open-uri' + + is_native = false + remote_site = remote_node.site + local_node_id = local_node.id + last_sync = remote_node.last_sync.to_i + + remote_objects(remote_site, local_node_id, last_sync, Tenant).each do |tenant| + puts "Processing Tenant: #{tenant[:name]}" + end + + remote_objects(remote_site, local_node_id, last_sync, UserGroup).each do |remote_object| + attributes = make_hash(remote_object.attributes) + tenant = Tenant.where(:name => attributes[:tenant]).first + process_object(UserGroup, tenant.user_groups, UserGroup.where(:name => attributes[:name], :tenant_id => tenant.try(:id)).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, User).each do |remote_object| + attributes = make_hash(remote_object.attributes) + + tenant = Tenant.where(:name => attributes[:current_tenant]).first + attributes[:language_id] = Language.where(:code => attributes[:language]).first.try(:id) + attributes.delete(:language) + attributes.delete(:current_tenant) + + if tenant + if ! attributes[:gs_node].blank? + attributes[:gs_node_id] = GsNode.where(:name => attributes[:gs_node]).first.try(:id) + attributes.delete(:gs_node) + end + + process_object(User, tenant.users, User.where(:uuid => attributes[:uuid]).first, attributes, { :is_native => is_native }) + else + $stderr.puts "NO_PROCESSING User #{attributes[:uuid]} - no current tenant" + end + end + + remote_objects(remote_site, local_node_id, last_sync, UserGroupMembership).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:user_id] = User.where(:uuid => attributes[:user_uuid]).first.try(:id) + attributes[:user_group_id] = UserGroup.where(:name => attributes[:user_group]).first.try(:id) + attributes.delete(:user_uuid) + attributes.delete(:user_group) + + if attributes[:user_id] && attributes[:user_group_id] + process_object(UserGroupMembership, UserGroupMembership, UserGroupMembership.where(:user_id => attributes[:user_id], :user_group_id => attributes[:user_group_id]).first, attributes) + end + end + + remote_objects(remote_site, local_node_id, last_sync, SipAccount).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:tenant_id] = Tenant.where(:name => attributes[:tenant]).first.try(:id) + attributes[:sip_domain] = SipDomain.where(:host => attributes[:sip_domain]).first + + if ! attributes[:sip_accountable_uuid].blank? + attributes[:sip_accountable_id] = attributes[:sip_accountable_type].constantize.where(:uuid => attributes[:sip_accountable_uuid]).first.try(:id) + end + + if attributes[:sip_accountable_id] + if ! attributes[:gs_node].blank? + attributes[:gs_node_id] = GsNode.where(:name => attributes[:gs_node]).first.try(:id) + attributes.delete(:gs_node) + end + + attributes.delete(:sip_accountable_uuid) + attributes.delete(:tenant) + process_object(SipAccount, SipAccount, SipAccount.where(:uuid => attributes[:uuid]).first, attributes, { :is_native => is_native }) + end + end + + remote_objects(remote_site, local_node_id, last_sync, Conference).each do |remote_object| + attributes = make_hash(remote_object.attributes) + + if ! attributes[:conferenceable_uuid].blank? + attributes[:conferenceable_id] = attributes[:conferenceable_type].constantize.where(:uuid => attributes[:conferenceable_uuid]).first.try(:id) + end + attributes.delete(:conferenceable_uuid) + + process_object(Conference, Conference, Conference.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, FaxAccount).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:tenant_id] = Tenant.where(:name => attributes[:tenant]).first.try(:id) + if ! attributes[:fax_accountable_uuid].blank? + attributes[:fax_accountable_id] = attributes[:fax_accountable_type].constantize.where(:uuid => attributes[:fax_accountable_uuid]).first.try(:id) + end + attributes.delete(:fax_accountable_uuid) + attributes.delete(:tenant) + process_object(FaxAccount, FaxAccount, FaxAccount.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, PhoneBook).each do |remote_object| + attributes = make_hash(remote_object.attributes) + + if ! attributes[:phone_bookable_uuid].blank? + attributes[:phone_bookable_id] = attributes[:phone_bookable_type].constantize.where(:uuid => attributes[:phone_bookable_uuid]).first.try(:id) + end + attributes.delete(:phone_bookable_uuid) + process_object(PhoneBook, PhoneBook, PhoneBook.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, PhoneBookEntry).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:phone_book_id] = PhoneBook.where(:uuid => attributes[:phone_book_uuid]).first.try(:id) + attributes.delete(:phone_book_uuid) + process_object(PhoneBookEntry, PhoneBookEntry, PhoneBookEntry.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, PhoneNumber).each do |remote_object| + attributes = make_hash(remote_object.attributes) + + if ! attributes[:phone_numberable_uuid].blank? + attributes[:phone_numberable_id] = attributes[:phone_numberable_type].constantize.where(:uuid => attributes[:phone_numberable_uuid]).first.try(:id) + end + + if ! attributes[:gs_node].blank? + attributes[:gs_node_id] = GsNode.where(:name => attributes[:gs_node]).first.try(:id) + attributes.delete(:gs_node) + end + + if !attributes[:phone_numberable_id] + puts "WARNING PhoneNumber #{attributes[:number]} has no local parent object #{attributes[:phone_numberable_type]}/#{attributes[:phone_numberable_uuid]}" + end + + attributes.delete(:phone_numberable_uuid) + process_object(PhoneNumber, PhoneNumber, PhoneNumber.where(:uuid => attributes[:uuid]).first, attributes, { :is_native => is_native }) + end + + remote_objects(remote_site, local_node_id, last_sync, CallForward).each do |remote_object| + attributes = make_hash(remote_object.attributes) + + attributes[:phone_number_id] = PhoneNumber.where(:uuid => attributes[:phone_number_uuid]).first.try(:id) + + if ! attributes[:call_forwardable_uuid].blank? + attributes[:call_forwardable_id] = attributes[:call_forwardable_type].constantize.where(:uuid => attributes[:call_forwardable_uuid]).first.try(:id) + attributes.delete(:call_forwardable_uuid) + end + + attributes[:call_forward_case_id] = CallForwardCase.where(:value => attributes[:service]).first.try(:id) + + attributes.delete(:phone_number_uuid) + attributes.delete(:service) + + process_object(CallForward, CallForward, CallForward.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, Softkey).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:sip_account_id] = SipAccount.where(:uuid => attributes[:sip_account_uuid]).first.try(:id) + attributes[:call_forward_id] = CallForward.where(:uuid => attributes[:call_forward_uuid]).first.try(:id) + attributes[:softkey_function_id] = SoftkeyFunction.where(:name => attributes[:function]).first.try(:id) + attributes.delete(:sip_account_uuid) + attributes.delete(:call_forward_uuid) + attributes.delete(:softkey_function) + process_object(Softkey, Softkey, Softkey.where(:uuid => attributes[:uuid]).first, attributes) + end + + remote_objects(remote_site, local_node_id, last_sync, Ringtone).each do |remote_object| + attributes = make_hash(remote_object.attributes) + if ! attributes[:ringtoneable_uuid].blank? + attributes[:ringtoneable_id] = attributes[:ringtoneable_type].constantize.where(:uuid => attributes[:ringtoneable_uuid]).first.try(:id) + end + + if !attributes[:ringtoneable_id] + puts "WARNING Ringtone #{attributes[:number]} has no local parent object #{attributes[:ringtoneable_type]}/#{attributes[:ringtoneable_uuid]}" + else + attributes.delete(:ringtoneable_uuid) + process_object(Ringtone, Ringtone, Ringtone.where(:ringtoneable_type => attributes[:ringtoneable_type], :ringtoneable_id => attributes[:ringtoneable_id]).first, attributes) + end + end + + remote_objects(remote_site, local_node_id, last_sync, ConferenceInvitee).each do |remote_object| + attributes = make_hash(remote_object.attributes) + attributes[:conference_id] = Conference.where(:uuid => attributes[:conference_uuid]).first.try(:id) + attributes[:phone_number] = PhoneNumber.where(:uuid => attributes[:phone_number_uuid]).first + if !attributes[:conference_id] + puts "WARNING ConferenceInvitee #{attributes[:uuid]} has no local Conference object #{attributes[:conference_uuid]}" + else + attributes[:phone_book_entry_id] = PhoneBookEntry.where(:uuid => attributes[:phone_book_entry_uuid]).first.try(:id) + attributes.delete(:conference_uuid) + attributes.delete(:phone_number_uuid) + attributes.delete(:phone_book_entry_uuid) + process_object(ConferenceInvitee, ConferenceInvitee, ConferenceInvitee.where(:uuid => attributes[:uuid]).first, attributes) + end + end + + #remote_objects(remote_site, local_node_id, last_sync, FaxDocument).each do |remote_object| + # attributes = make_hash(remote_object.attributes) + # attributes[:fax_account_id] = FaxAccount.where(:uuid => attributes[:fax_account_uuid]).first.try(:id) + # attributes[:fax_resolution_id] = FaxResolution.where(:resolution_value => attributes[:fax_resolution]).first.try(:id) + # attributes.delete(:fax_account_uuid) + # attributes.delete(:fax_resolution) + # process_object(FaxDocument, FaxDocument, FaxDocument.where(:uuid => attributes[:uuid]).first, attributes) + #end + + #remote_objects(remote_site, local_node_id, last_sync, CallHistory).each do |remote_object| + # attributes = make_hash(remote_object.attributes) + # process_object(CallHistory, CallHistory, CallHistory.where(:caller_channel_uuid => attributes[:caller_channel_uuid], :call_historyable_type => attributes[:caller_channel_type], :call_historyable_id => call_historyable.try(:id)).first, attributes) + #end + + remote_objects(remote_site, local_node_id, last_sync, DeletedItem).each do |remote_object| + attributes = make_hash(remote_object.attributes) + deleted_item = remote_object[:class_name].constantize.where(:uuid => attributes[:uuid]).first + + if deleted_item + print "DELETE #{deleted_item.class.to_s} #{deleted_item.to_s} : " + + if deleted_item.destroy + puts "OK" + else + $stderr.puts "Couldn't delete #{deleted_item.class.to_s}. #{deleted_item.errors.inspect}" + end + else + puts "NO_DELETE #{remote_object[:class_name]} #{remote_object[:uuid]}" + end + end + + if ! remote_node.synced + $stderr.puts "Errors updating node #{remote_node.name}. #{remote_node.errors.inspect}" + end + end + + def make_hash(attributes, new_hash = Hash.new) + attributes.each do |key, value| + new_hash[key.to_sym] = value.to_s + end + return new_hash + end + + def remote_objects(remote_site, local_node_id, last_sync, object_class) + class_name = object_class.to_s.underscore + section_name = class_name.pluralize + doc = Nokogiri::XML(open("#{remote_site}/gs_nodes/#{local_node_id}/sync.xml?newer=#{last_sync}&image=false&class=#{section_name}", :proxy => nil, :read_timeout => 120)) + return doc.xpath("//gemeinschaft_sync/#{section_name}/#{class_name}") + end + + def process_object(object_class, belongs_to, local_object, attributes, local_attributes = Hash.new) + if local_object + if local_object.updated_at < attributes[:updated_at] + print "UPDATE #{object_class.to_s} #{local_object.to_s} : " + update_object(local_object, attributes, local_attributes) + else + print "NO_UPDATE #{object_class.to_s}: #{local_object.to_s} - last update: #{local_object.updated_at}, remote: #{attributes[:updated_at]}" + end + else + print "CREATE #{object_class.to_s} #{attributes[:name].to_s} #{attributes[:uuid].to_s} : " + create_object(belongs_to, attributes, local_attributes) + end + puts "." + end + + def create_object(object_class, attributes, local_attributes) + attributes = attributes.merge(local_attributes) + + new_local_copy = object_class.create(attributes, :without_protection => true) + if new_local_copy && new_local_copy.errors.count == 0 + print "Created object, #{new_local_copy.class.to_s} #{new_local_copy.to_s}" + return true + else + $stderr.print "Couldn't create object. #{new_local_copy.errors.messages.inspect}" + return false + end + end + + def update_object(local_object, attributes, local_attributes) + attributes = attributes.merge(local_attributes) + + if local_object.update_attributes(attributes, :without_protection => true) + print "Updated #{local_object.class.to_s}, ID #{local_object.id}." + return true + else + $stderr.print "Couldn't update UserGroup. #{local_user_group.errors.inspect}" + return true + end + end + + class DeletedItem + end +end |