diff --git a/app/models/blacklisted_event.rb b/app/models/blacklisted_event.rb new file mode 100644 index 00000000000..3cbc3bdbea5 --- /dev/null +++ b/app/models/blacklisted_event.rb @@ -0,0 +1,54 @@ +class BlacklistedEvent < ActiveRecord::Base + belongs_to :ext_management_system, :foreign_key => "ems_id" + + default_value_for :enabled, true + after_save :queue_sync_blacklisted_event_names + after_destroy :queue_sync_blacklisted_event_names, :audit_deletion + after_create :audit_creation + + def audit_deletion + $audit_log.info("Blacklisted event [#{event_name}] for provider [#{provider_model}] with ID [#{ems_id}] has been deleted by user [#{self.class.current_userid}]") + end + + def audit_creation + $audit_log.info("Creating blacklisted event [#{event_name}] for provider [#{provider_model}] with ID [#{ems_id}] by user [#{self.class.current_userid}]") + end + + def enabled=(value) + return if enabled == value + + super + $audit_log.info("Blacklisted event [#{event_name}] for provider [#{provider_model}] with ID [#{ems_id}] had enabled changed to #{value} by user [#{self.class.current_userid}]") + end + + def self.seed + MiqRegion.my_region.lock do + ExtManagementSystem.descendants.each do |ems| + missing_events = ems.default_blacklisted_event_names - where(:provider_model => ems.name, :ems_id => nil).pluck(:event_name) + create(missing_events.collect { |e| {:event_name => e, :provider_model => ems.name, :system => true} }) + end + end + end + + def self.current_userid + User.current_userid || 'system' + end + + def queue_sync_blacklisted_event_names + # notify MiqServer to sync with the blacklisted events + servers = MiqRegion.my_region.active_miq_servers + return if servers.blank? + _log.info("Queueing sync_blacklisted_event_names for [#{servers.length}] active_miq_servers, ids: #{servers.collect(&:id)}") + + servers.each do |s| + MiqQueue.put( + :class_name => "MiqServer", + :instance_id => s.id, + :method_name => "sync_blacklisted_event_names", + :server_guid => s.guid, + :priority => MiqQueue::HIGH_PRIORITY, + :queue_name => 'miq_server' + ) + end + end +end diff --git a/app/models/ems_event.rb b/app/models/ems_event.rb index 1a63298d5e9..cc8d93c9238 100644 --- a/app/models/ems_event.rb +++ b/app/models/ems_event.rb @@ -34,10 +34,6 @@ def self.bottleneck_event_groups VMDB::Config.new('event_handling').config[:bottleneck_event_groups] end - def self.filtered_events - VMDB::Config.new('event_handling').config[:filtered_events] - end - def self.group_and_level(event_type) group, v = self.event_groups.find { |k, v| v[:critical].include?(event_type) || v[:detail].include?(event_type) } if group.nil? @@ -102,8 +98,6 @@ def self.add(ems_id, event_hash) process_availability_zone_in_event!(event_hash) process_cluster_in_event!(event_hash) - return if events_filtered?(event_hash, self.filtered_events[event_type.to_sym]) - # Write the event new_event = create_event(event_hash) @@ -113,34 +107,6 @@ def self.add(ems_id, event_hash) return new_event end - FILTER_KEYS = [ - # Filter Key | Event Key | Object Description - #======================================================== - ['ems', :ems_id, "EMS" ], - ['src_vm', :vm_or_template_id, "source VM" ], - ['dest_vm', :dest_vm_or_template_id, "dest VM" ], - ['src_host', :host_id, "source Host" ], - ['dest_host', :dest_host_id, "dest Host" ] - ] - - def self.events_filtered?(event_hash, filter) - return false if filter.nil? - - log_prefix = "Skipping caught event [#{event_hash[:event_type]}] chainId [#{event_hash[:chain_id]}]" - FILTER_KEYS.each do |filter_key, event_key, object_description| - if event_filtered?(event_hash, event_key, filter, filter_key) - _log.info "#{log_prefix} for #{object_description} [#{event_hash[event_key]}]" - return true - end - end - - return false - end - - def self.event_filtered?(event_hash, event_hash_key, filter, filter_key) - filter.has_key?(filter_key) && filter[filter_key].include?(event_hash[event_hash_key]) - end - def self.process_object_in_event!(klass, event, options = {}) prefix = options[:prefix] key_prefix = options[:key_prefix] || klass.name.underscore diff --git a/app/models/ext_management_system.rb b/app/models/ext_management_system.rb index 1b7a0908a91..358d865a0b5 100644 --- a/app/models/ext_management_system.rb +++ b/app/models/ext_management_system.rb @@ -36,6 +36,7 @@ def self.supported_types_and_descriptions_hash has_many :ems_events, -> { order "timestamp" }, :class_name => "EmsEvent", :foreign_key => "ems_id" has_many :policy_events, -> { order "timestamp" }, :class_name => "PolicyEvent", :foreign_key => "ems_id" + has_many :blacklisted_events, :foreign_key => "ems_id", :dependent => :destroy has_many :ems_folders, :foreign_key => "ems_id", :dependent => :destroy has_many :ems_clusters, :foreign_key => "ems_id", :dependent => :destroy has_many :resource_pools, :foreign_key => "ems_id", :dependent => :destroy @@ -186,6 +187,10 @@ def self.provision_workflow_class end end + def self.default_blacklisted_event_names + [] + end + # UI methods for determining availability of fields def supports_port? false @@ -477,4 +482,15 @@ def stop_event_monitor_queue_on_credential_change self.stop_event_monitor_queue end end + + def blacklisted_event_names + ( + self.class.blacklisted_events.where(:enabled => true).pluck(:event_name) + + blacklisted_events.where(:enabled => true).pluck(:event_name) + ).uniq.sort + end + + def self.blacklisted_events + BlacklistedEvent.where(:provider_model => name, :ems_id => nil) + end end diff --git a/app/models/manageiq/providers/amazon/cloud_manager.rb b/app/models/manageiq/providers/amazon/cloud_manager.rb index 571d9103366..532674b18f8 100644 --- a/app/models/manageiq/providers/amazon/cloud_manager.rb +++ b/app/models/manageiq/providers/amazon/cloud_manager.rb @@ -31,6 +31,14 @@ def self.hostname_required? false end + def self.default_blacklisted_event_names + %w( + ConfigurationSnapshotDeliveryCompleted + ConfigurationSnapshotDeliveryStarted + ConfigurationSnapshotDeliveryFailed + ) + end + validates :provider_region, :inclusion => {:in => ManageIQ::Providers::Amazon::Regions.names} def description diff --git a/app/models/manageiq/providers/base_manager/event_catcher/runner.rb b/app/models/manageiq/providers/base_manager/event_catcher/runner.rb index 9cbcb7c176d..ade530d89c8 100644 --- a/app/models/manageiq/providers/base_manager/event_catcher/runner.rb +++ b/app/models/manageiq/providers/base_manager/event_catcher/runner.rb @@ -16,11 +16,9 @@ def after_initialize do_exit("Unable to find instance for EMS ID [#{@cfg[:ems_id]}].", 1) if @ems.nil? do_exit("EMS ID [#{@cfg[:ems_id]}] failed authentication check.", 1) unless @ems.authentication_check.first - # Get the filtered events from the event_handling config - @filtered_events = VMDB::Config.new("event_handling").config[:filtered_events] - @filtered_events = @filtered_events.each_with_object([]) { |(k, v), ary| ary << k.to_s if v.nil? } + @filtered_events = @ems.blacklisted_event_names _log.info "#{log_prefix} Event Catcher skipping the following events:" - $log.log_hashes(@filtered_events.sort) + $log.log_hashes(@filtered_events) # Global Work Queue @queue = Queue.new @@ -64,6 +62,22 @@ def filtered_events @filtered_events end + # Called when there is any change in BlacklistedEvent + def sync_blacklisted_events + return unless @ems + filters = @ems.blacklisted_event_names + + if @filtered_events.nil? || @filtered_events != filters + adds = filters - @filtered_events + deletes = @filtered_events - filters + + @filtered_events = filters + _log.info("Synchronizing blacklisted events: #{filters}") + _log.info(" Blacklisted events added: #{adds}") + _log.info(" Blacklisted events deleted: #{deletes}") + end + end + def stop_event_monitor raise NotImplementedError, "must be implemented in subclass" end diff --git a/app/models/manageiq/providers/openstack/cloud_manager.rb b/app/models/manageiq/providers/openstack/cloud_manager.rb index 2508836ad1c..fa266431ec4 100644 --- a/app/models/manageiq/providers/openstack/cloud_manager.rb +++ b/app/models/manageiq/providers/openstack/cloud_manager.rb @@ -32,6 +32,14 @@ def self.description @description ||= "OpenStack".freeze end + def self.default_blacklisted_event_names + %w( + scheduler.run_instance.start + scheduler.run_instance.scheduled + scheduler.run_instance.end + ) + end + def supports_port? true end diff --git a/app/models/manageiq/providers/redhat/infra_manager.rb b/app/models/manageiq/providers/redhat/infra_manager.rb index 604f3a2771a..b512bba4e16 100644 --- a/app/models/manageiq/providers/redhat/infra_manager.rb +++ b/app/models/manageiq/providers/redhat/infra_manager.rb @@ -21,6 +21,17 @@ def self.description @description ||= "Red Hat Enterprise Virtualization Manager".freeze end + def self.default_blacklisted_event_names + %w( + UNASSIGNED + USER_REMOVE_VG + USER_REMOVE_VG_FAILED + USER_VDC_LOGIN + USER_VDC_LOGOUT + USER_VDC_LOGIN_FAILED + ) + end + def supports_port? true end diff --git a/app/models/manageiq/providers/vmware/infra_manager.rb b/app/models/manageiq/providers/vmware/infra_manager.rb index 8e62fb1a141..8b244351171 100644 --- a/app/models/manageiq/providers/vmware/infra_manager.rb +++ b/app/models/manageiq/providers/vmware/infra_manager.rb @@ -79,6 +79,26 @@ def self.provision_class(via) end end + def self.default_blacklisted_event_names + %w( + AlarmActionTriggeredEvent + AlarmCreatedEvent + AlarmEmailCompletedEvent + AlarmEmailFailedEvent + AlarmReconfiguredEvent + AlarmRemovedEvent + AlarmScriptCompleteEvent + AlarmScriptFailedEvent + AlarmSnmpCompletedEvent + AlarmSnmpFailedEvent + AlarmStatusChangedEvent + AlreadyAuthenticatedSessionEvent + EventEx + UserLoginSessionEvent + UserLogoutSessionEvent + ) + end + def control_monitor MiqControlMonitor.find_by_ems(self) end diff --git a/app/models/miq_server/configuration_management.rb b/app/models/miq_server/configuration_management.rb index e25ef291746..7943c6b19a9 100644 --- a/app/models/miq_server/configuration_management.rb +++ b/app/models/miq_server/configuration_management.rb @@ -64,6 +64,7 @@ def set_config_remote(cfg) end def sync_config + @blacklisted_events = true @vmdb_config = VMDB::Config.new("vmdb") sync_log_level sync_worker_monitor_settings @@ -77,7 +78,10 @@ def sync_config_changed? VMDB::Config.invalidate("vmdb") @vmdb_config = VMDB::Config.new("vmdb") end - stale + stale || @blacklisted_events.nil? end + def sync_blacklisted_event_names + @blacklisted_events = nil + end end diff --git a/config/event_handling.tmpl.yml b/config/event_handling.tmpl.yml index a8605a3f52c..791745d5f04 100644 --- a/config/event_handling.tmpl.yml +++ b/config/event_handling.tmpl.yml @@ -1413,42 +1413,6 @@ event_handling: - refresh: - ems -# -# The filtered_events section defines which events should not be added to the -# database. Keys that have nil values will be filtered at the event catcher -# level. Keys that do have values, in the format of a hash of object type to -# an array of ids, will be filtered such that they are not added to the event -# table. -# -filtered_events: - AlarmActionTriggeredEvent: # Vmware filtered events - AlarmCreatedEvent: - AlarmEmailCompletedEvent: - AlarmEmailFailedEvent: - AlarmReconfiguredEvent: - AlarmRemovedEvent: - AlarmScriptCompleteEvent: - AlarmScriptFailedEvent: - AlarmSnmpCompletedEvent: - AlarmSnmpFailedEvent: - AlarmStatusChangedEvent: - AlreadyAuthenticatedSessionEvent: - EventEx: - UNASSIGNED: # RHEV filtered events - USER_REMOVE_VG: - USER_REMOVE_VG_FAILED: - USER_VDC_LOGIN: - USER_VDC_LOGOUT: - USER_VDC_LOGIN_FAILED: - UserLoginSessionEvent: - UserLogoutSessionEvent: - scheduler.run_instance.start: # OpenStack filtered events - scheduler.run_instance.scheduled: - scheduler.run_instance.end: - ConfigurationSnapshotDeliveryCompleted: # Amazon filtered events - ConfigurationSnapshotDeliveryStarted: - ConfigurationSnapshotDeliveryFailed: - # # The task_final_events section defines which events could be the final event # for a task, and the list of those tasks for which that event is a final event. diff --git a/db/migrate/20150804194147_create_blacklisted_events.rb b/db/migrate/20150804194147_create_blacklisted_events.rb new file mode 100644 index 00000000000..ec6699f0c46 --- /dev/null +++ b/db/migrate/20150804194147_create_blacklisted_events.rb @@ -0,0 +1,12 @@ +class CreateBlacklistedEvents < ActiveRecord::Migration + def change + create_table :blacklisted_events do |t| + t.string :event_name + t.string :provider_model + t.bigint :ems_id + t.boolean :system + t.boolean :enabled + t.timestamps + end + end +end diff --git a/db/migrate/20150806194147_migrate_filtered_events_to_blacklisted_events.rb b/db/migrate/20150806194147_migrate_filtered_events_to_blacklisted_events.rb new file mode 100644 index 00000000000..ce623722d63 --- /dev/null +++ b/db/migrate/20150806194147_migrate_filtered_events_to_blacklisted_events.rb @@ -0,0 +1,71 @@ +class MigrateFilteredEventsToBlacklistedEvents < ActiveRecord::Migration + class Configuration < ActiveRecord::Base + serialize :settings + self.inheritance_column = :_type_disabled + end + + class BlacklistedEvent < ActiveRecord::Base + self.inheritance_column = :_type_disabled + end + + def up + say_with_time('Migrating filtered events from Configuration to BlacklistedEvent') do + events = [] + Configuration.where(:typ => 'event_handling').each do |config| + filtered_events = config.settings.fetch_path('filtered_events') + next unless filtered_events + + events << filtered_events.each_with_object([]) { |(k, v), ary| ary << k.to_s if v.nil? } + + config.settings.delete_path('filtered_events') + config.save + end + + user_adds = events.flatten.uniq - default_blacklisted_event_names + user_adds.each do |e| + BlacklistedEvent.create!(PROVIDER_NAMES.collect { |p| {:event_name => e, :provider_model => p} }) + end + end + end + + private + + PROVIDER_NAMES = %w( + ManageIQ::Providers::Openstack::CloudManager + ManageIQ::Providers::Amazon::CloudManager + ManageIQ::Providers::Redhat::InfraManager + ManageIQ::Providers::Vmware::InfraManager + ) + + def default_blacklisted_event_names + %w( + scheduler.run_instance.start + scheduler.run_instance.scheduled + scheduler.run_instance.end + ConfigurationSnapshotDeliveryCompleted + ConfigurationSnapshotDeliveryStarted + ConfigurationSnapshotDeliveryFailed + UNASSIGNED + USER_REMOVE_VG + USER_REMOVE_VG_FAILED + USER_VDC_LOGIN + USER_VDC_LOGOUT + USER_VDC_LOGIN_FAILED + AlarmActionTriggeredEvent + AlarmCreatedEvent + AlarmEmailCompletedEvent + AlarmEmailFailedEvent + AlarmReconfiguredEvent + AlarmRemovedEvent + AlarmScriptCompleteEvent + AlarmScriptFailedEvent + AlarmSnmpCompletedEvent + AlarmSnmpFailedEvent + AlarmStatusChangedEvent + AlreadyAuthenticatedSessionEvent + EventEx + UserLoginSessionEvent + UserLogoutSessionEvent + ) + end +end diff --git a/lib/workers/worker_base.rb b/lib/workers/worker_base.rb index 3e5915bf114..43872c71534 100644 --- a/lib/workers/worker_base.rb +++ b/lib/workers/worker_base.rb @@ -280,6 +280,7 @@ def sync_config(config = nil) @my_zone ||= MiqServer.my_zone sync_log_level sync_worker_settings + sync_blacklisted_events _log.info("ID [#{@worker.id}], PID [#{Process.pid}], GUID [#{@worker.guid}], Zone [#{@my_zone}], Active Roles [#{@active_roles.join(',')}], Assigned Roles [#{MiqServer.my_role}], Configuration:") $log.log_hashes(@worker_settings) $log.info("---") @@ -390,6 +391,9 @@ def after_sync_active_roles def before_exit(message, exit_code) end + def sync_blacklisted_events + end + # # Polling methods # diff --git a/spec/factories/blacklisted_event.rb b/spec/factories/blacklisted_event.rb new file mode 100644 index 00000000000..649bcbb15c1 --- /dev/null +++ b/spec/factories/blacklisted_event.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :blacklisted_event do + enabled true + end +end diff --git a/spec/migrations/20150806194147_migrate_filtered_events_to_blacklisted_events_spec.rb b/spec/migrations/20150806194147_migrate_filtered_events_to_blacklisted_events_spec.rb new file mode 100644 index 00000000000..72a7303291d --- /dev/null +++ b/spec/migrations/20150806194147_migrate_filtered_events_to_blacklisted_events_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' +require Rails.root.join('db/migrate/20150806194147_migrate_filtered_events_to_blacklisted_events.rb') + +describe MigrateFilteredEventsToBlacklistedEvents do + let(:configuration_stub) { migration_stub(:Configuration) } + + migration_context :up do + it 'when filtered events section exists with system events' do + event_settings = { + 'filtered_events' => { + :AlarmActionTriggeredEvent => nil, + :AlarmCreatedEvent => nil + } + } + configuration_stub.create!(:typ => 'event_handling', :settings => event_settings) + + migrate + + expect(Configuration.first.settings.fetch_path('filtered_events')).to be_blank + expect(BlacklistedEvent.count).to eq(0) + end + + it 'when filtered events section exists with user added events' do + event_settings = { + 'filtered_events' => { + :SomeNewEvent => nil + } + } + configuration_stub.create!(:typ => 'event_handling', :settings => event_settings) + + migrate + + expect(Configuration.first.settings.fetch_path('filtered_events')).to be_blank + expect(BlacklistedEvent.count).to eq(MigrateFilteredEventsToBlacklistedEvents::PROVIDER_NAMES.size) + end + + it 'when filtered events section does not exist in configuration' do + configuration_stub.create!(:typ => 'event_handling', :settings => {:event_groups => {'a' => nil }}) + + migrate + + expect(Configuration.first.settings.fetch_path('filtered_events')).to be_blank + expect(BlacklistedEvent.count).to eq(0) + end + + it 'when event handling configuration does not exist' do + migrate + + expect(BlacklistedEvent.count).to eq(0) + end + + it 'when multiple event handling configurations exist' do + configuration_stub.create!(:typ => 'event_handling', :settings => { 'filtered_events' => { :user_event_1 => nil } }) + configuration_stub.create!(:typ => 'event_handling', :settings => { 'filtered_events' => { :user_event_2 => nil } }) + + migrate + + expect(Configuration.first.settings.fetch_path('filtered_events')).to be_blank + expect(BlacklistedEvent.count).to eq(2 * MigrateFilteredEventsToBlacklistedEvents::PROVIDER_NAMES.size) + end + end +end diff --git a/spec/models/blacklisted_event_spec.rb b/spec/models/blacklisted_event_spec.rb new file mode 100644 index 00000000000..9d1143db412 --- /dev/null +++ b/spec/models/blacklisted_event_spec.rb @@ -0,0 +1,46 @@ +require 'spec_helper' +require 'workers/event_catcher' + +describe BlacklistedEvent do + let(:total_blacklist_entry_count) { ExtManagementSystem.descendants.collect(&:default_blacklisted_event_names).flatten.count } + before do + MiqRegion.seed + end + + context '.seed' do + it 'loads event filters' do + described_class.seed + expect(described_class.count).to eq(total_blacklist_entry_count) + end + + it 're-seeds deleted event filters' do + described_class.seed + described_class.where(:event_name => 'AlarmCreatedEvent').destroy_all + expect(described_class.count).to eq(total_blacklist_entry_count - 1) + + described_class.seed + expect(described_class.count).to eq(total_blacklist_entry_count) + end + + it 'does not re-seed existing event filters' do + User.stub(:current_userid).and_return('admin') + filter = FactoryGirl.create(:blacklisted_event, + :event_name => 'AlarmActionTriggeredEvent', + :provider_model => 'ManageIQ::Providers::Vmware::InfraManager' + ) + filter_attrs = filter.attributes + + described_class.seed + expect(filter.attributes).to eq(filter_attrs) + end + end + + it '#enabled=' do + User.stub(:current_userid).and_return('test_user') + f = FactoryGirl.create(:blacklisted_event, :event_name => 'event_1') + expect(f.enabled).to be_true + + f.enabled = false + expect(f.enabled).to be_false + end +end