From f48e742d2375b78a02b542fd0fe20c1ec52970e6 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Tue, 12 Apr 2016 13:44:28 +0200 Subject: [PATCH] Added icinga integration. --- .../controllers/_integration/icinga.coffee | 12 ++ .../_integration/mattermost.coffee | 12 ++ .../controllers/_integration/nagios.coffee | 12 ++ .../app/controllers/integrations.coffee | 8 + .../app/controllers/settings.coffee | 11 +- app/models/channel/email_parser.rb | 94 +++------ app/models/channel/filter/icinga.rb | 77 +++++++ app/models/channel/filter/identify_sender.rb | 88 ++++++++ .../20160412000002_add_ichinga_integration.rb | 152 ++++++++++++++ db/seeds.rb | 103 ++++++++++ test/unit/email_process_test.rb | 4 + test/unit/integration_icinga_test.rb | 193 ++++++++++++++++++ 12 files changed, 692 insertions(+), 74 deletions(-) create mode 100644 app/assets/javascripts/app/controllers/_integration/icinga.coffee create mode 100644 app/assets/javascripts/app/controllers/_integration/mattermost.coffee create mode 100644 app/assets/javascripts/app/controllers/_integration/nagios.coffee create mode 100644 app/assets/javascripts/app/controllers/integrations.coffee create mode 100644 app/models/channel/filter/icinga.rb create mode 100644 app/models/channel/filter/identify_sender.rb create mode 100644 db/migrate/20160412000002_add_ichinga_integration.rb create mode 100644 test/unit/integration_icinga_test.rb diff --git a/app/assets/javascripts/app/controllers/_integration/icinga.coffee b/app/assets/javascripts/app/controllers/_integration/icinga.coffee new file mode 100644 index 000000000..a5ad04487 --- /dev/null +++ b/app/assets/javascripts/app/controllers/_integration/icinga.coffee @@ -0,0 +1,12 @@ +class Icinga extends App.ControllerTabs + header: 'Icinga' + constructor: -> + super + return if !@authenticate(false, 'Admin') + @title 'Icinga', true + @tabs = [ + { name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Integration::Icinga' } } + ] + @render() + +App.Config.set('IntegrationIcinga', { prio: 1100, parent: '#integration', name: 'Icinga', target: '#integration/icinga', controller: Icinga, role: ['Admin'] }, 'NavBarIntegration') diff --git a/app/assets/javascripts/app/controllers/_integration/mattermost.coffee b/app/assets/javascripts/app/controllers/_integration/mattermost.coffee new file mode 100644 index 000000000..ce340da0d --- /dev/null +++ b/app/assets/javascripts/app/controllers/_integration/mattermost.coffee @@ -0,0 +1,12 @@ +class Mattermost extends App.ControllerTabs + header: 'Mattermost' + constructor: -> + super + return if !@authenticate(false, 'Admin') + @title 'Mattermost', true + @tabs = [ + { name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Integration::Mattermost' } } + ] + @render() + +App.Config.set('IntegrationMattermost', { prio: 1000, parent: '#integration', name: 'Mattermost', target: '#integration/mattermost', controller: Mattermost, role: ['Admin'] }, 'NavBarIntegration') diff --git a/app/assets/javascripts/app/controllers/_integration/nagios.coffee b/app/assets/javascripts/app/controllers/_integration/nagios.coffee new file mode 100644 index 000000000..471fa8516 --- /dev/null +++ b/app/assets/javascripts/app/controllers/_integration/nagios.coffee @@ -0,0 +1,12 @@ +class Nagios extends App.ControllerTabs + header: 'Nagios' + constructor: -> + super + return if !@authenticate(false, 'Admin') + @title 'Nagios', true + @tabs = [ + { name: 'Base', 'target': 'base', controller: App.SettingsArea, params: { area: 'Integration::Nagios' } } + ] + @render() + +App.Config.set('IntegrationNagios', { prio: 1200, parent: '#integration', name: 'Nagios', target: '#integration/nagios', controller: Nagios, role: ['Admin'] }, 'NavBarIntegration') diff --git a/app/assets/javascripts/app/controllers/integrations.coffee b/app/assets/javascripts/app/controllers/integrations.coffee new file mode 100644 index 000000000..5de52cf83 --- /dev/null +++ b/app/assets/javascripts/app/controllers/integrations.coffee @@ -0,0 +1,8 @@ +class IndexRouter extends App.ControllerNavSidbar + authenticateRequired: true + configKey: 'NavBarIntegration' + +App.Config.set('integration', IndexRouter, 'Routes') +App.Config.set('integration/:target', IndexRouter, 'Routes') + +App.Config.set('Integration', { prio: 1000, name: 'Integration', target: '#integration', role: ['Admin'] }, 'NavBarIntegration') diff --git a/app/assets/javascripts/app/controllers/settings.coffee b/app/assets/javascripts/app/controllers/settings.coffee index 5ab53271f..27c4eea27 100644 --- a/app/assets/javascripts/app/controllers/settings.coffee +++ b/app/assets/javascripts/app/controllers/settings.coffee @@ -62,9 +62,8 @@ class Ticket extends App.ControllerTabs ] @render() -App.Config.set( 'SettingBranding', { prio: 1200, parent: '#settings', name: 'Branding', target: '#settings/branding', controller: Branding, role: ['Admin'] }, 'NavBarAdmin' ) -App.Config.set( 'SettingSystem', { prio: 1400, parent: '#settings', name: 'System', target: '#settings/system', controller: System, role: ['Admin'] }, 'NavBarAdmin' ) -App.Config.set( 'SettingSecurity', { prio: 1600, parent: '#settings', name: 'Security', target: '#settings/security', controller: Security, role: ['Admin'] }, 'NavBarAdmin' ) -App.Config.set( 'SettingTicket', { prio: 1700, parent: '#settings', name: 'Ticket', target: '#settings/ticket', controller: Ticket, role: ['Admin'] }, 'NavBarAdmin' ) -App.Config.set( 'SettingImport', { prio: 1800, parent: '#settings', name: 'Import', target: '#settings/import', controller: Import, role: ['Admin'] }, 'NavBarAdmin' ) - +App.Config.set('SettingBranding', { prio: 1200, parent: '#settings', name: 'Branding', target: '#settings/branding', controller: Branding, role: ['Admin'] }, 'NavBarAdmin') +App.Config.set('SettingSystem', { prio: 1400, parent: '#settings', name: 'System', target: '#settings/system', controller: System, role: ['Admin'] }, 'NavBarAdmin') +App.Config.set('SettingSecurity', { prio: 1600, parent: '#settings', name: 'Security', target: '#settings/security', controller: Security, role: ['Admin'] }, 'NavBarAdmin') +App.Config.set('SettingTicket', { prio: 1700, parent: '#settings', name: 'Ticket', target: '#settings/ticket', controller: Ticket, role: ['Admin'] }, 'NavBarAdmin') +App.Config.set('SettingImport', { prio: 1800, parent: '#settings', name: 'Import', target: '#settings/import', controller: Import, role: ['Admin'] }, 'NavBarAdmin') diff --git a/app/models/channel/email_parser.rb b/app/models/channel/email_parser.rb index 226e5cec1..b2830cb6e 100644 --- a/app/models/channel/email_parser.rb +++ b/app/models/channel/email_parser.rb @@ -43,12 +43,18 @@ class Channel::EmailParser x-zammad-customer-firstname: '', x-zammad-customer-lastname: '', - # ticket headers + # ticket headers (for new tickets) x-zammad-ticket-group: 'some_group', x-zammad-ticket-state: 'some_state', x-zammad-ticket-priority: 'some_priority', x-zammad-ticket-owner: 'some_owner_login', + # ticket headers (for existing tickets) + x-zammad-ticket-followup-group: 'some_group', + x-zammad-ticket-followup-state: 'some_state', + x-zammad-ticket-followup-priority: 'some_priority', + x-zammad-ticket-followup-owner: 'some_owner_login', + # article headers x-zammad-article-internal: false, x-zammad-article-type: 'agent', @@ -66,7 +72,6 @@ class Channel::EmailParser # set all headers mail.header.fields.each { |field| - next if !field.name # full line, encode, ready for storage @@ -79,20 +84,15 @@ class Channel::EmailParser # get sender from = nil ['from', 'reply-to', 'return-path'].each { |item| - next if !mail[ item.to_sym ] - from = mail[ item.to_sym ].value - break if from } # set x-any-recipient data['x-any-recipient'.to_sym] = '' ['to', 'cc', 'delivered-to', 'x-original-to', 'envelope-to'].each { |item| - next if !mail[item.to_sym] - if data['x-any-recipient'.to_sym] != '' data['x-any-recipient'.to_sym] += ', ' end @@ -347,7 +347,7 @@ retrns # run postmaster pre filter filters = {} - Setting.where(area: 'Postmaster::PreFilter').each {|setting| + Setting.where(area: 'Postmaster::PreFilter').order(:name).each {|setting| filters[setting.name] = Kernel.const_get(Setting.get(setting.name)) } filters.each {|_prio, backend| @@ -373,35 +373,15 @@ retrns # reset current_user UserInfo.current_user_id = 1 - # create sender - if mail[ 'x-zammad-customer-login'.to_sym ] - user = User.find_by(login: mail[ 'x-zammad-customer-login'.to_sym ]) + # create sender if needed + sender_user_id = mail[ 'x-zammad-customer-id'.to_sym ] + if !sender_user_id + raise 'No x-zammad-customer-id, no sender set!' end + user = User.lookup(id: sender_user_id) if !user - user = User.find_by(email: mail[ 'x-zammad-customer-email'.to_sym ] || mail[:from_email]) + raise "No user found for x-zammad-customer-id: #{sender_user_id}!" end - if !user - user = user_create( - login: mail[ 'x-zammad-customer-login'.to_sym ] || mail[ 'x-zammad-customer-email'.to_sym ] || mail[:from_email], - firstname: mail[ 'x-zammad-customer-firstname'.to_sym ] || mail[:from_display_name], - lastname: mail[ 'x-zammad-customer-lastname'.to_sym ], - email: mail[ 'x-zammad-customer-email'.to_sym ] || mail[:from_email], - ) - end - - # create to and cc user - ['raw-to', 'raw-cc'].each { |item| - next if !mail[item.to_sym] - next if !mail[item.to_sym].addrs - items = mail[item.to_sym].addrs - items.each {|address_data| - user_create( - firstname: address_data.display_name, - lastname: '', - email: address_data.address, - ) - } - } # set current user UserInfo.current_user_id = user.id @@ -416,6 +396,8 @@ retrns # set ticket state to open if not new if ticket + set_attributes_by_x_headers(ticket, 'ticket', mail, 'followup') + state = Ticket::State.find(ticket.state_id) state_type = Ticket::StateType.find(state.state_type_id) @@ -425,9 +407,11 @@ retrns end # set ticket to open again - if state_type.name != 'new' && !mail[ 'x-zammad-out-of-office'.to_sym ] - ticket.state = Ticket::State.find_by(name: 'open') - ticket.save + if !mail[ 'x-zammad-ticket-followup-state'.to_sym ] + if state_type.name != 'new' && !mail[ 'x-zammad-out-of-office'.to_sym ] + ticket.state = Ticket::State.find_by(name: 'open') + ticket.save + end end end @@ -505,7 +489,7 @@ retrns Observer::Ticket::Notification.transaction # run postmaster post filter - Setting.where(area: 'Postmaster::PostFilter').each {|setting| + Setting.where(area: 'Postmaster::PostFilter').order(:name).each {|setting| filters[setting.name] = Kernel.const_get(Setting.get(setting.name)) } filters.each {|_prio, backend| @@ -521,36 +505,7 @@ retrns [ticket, article, user, mail] end - def user_create(data) - - # return existing - user = User.find_by(login: data[:email].downcase) - return user if user - - # create new user - roles = Role.where(name: 'Customer') - - # fillup - %w(firstname lastname).each { |item| - if data[item.to_sym].nil? - data[item.to_sym] = '' - end - } - data[:password] = '' - data[:active] = true - data[:roles] = roles - data[:updated_by_id] = 1 - data[:created_by_id] = 1 - - user = User.create(data) - user.update_attributes( - updated_by_id: user.id, - created_by_id: user.id, - ) - user - end - - def set_attributes_by_x_headers(item_object, header_name, mail) + def set_attributes_by_x_headers(item_object, header_name, mail, suffix = false) # loop all x-zammad-hedaer-* headers item_object.attributes.each {|key, _value| @@ -566,6 +521,9 @@ retrns if key_short == '_id' key_short = key[ 0, key.length - 3 ] header = "x-zammad-#{header_name}-#{key_short}" + if suffix + header = "x-zammad-#{header_name}-#{suffix}-#{key_short}" + end if mail[ header.to_sym ] Rails.logger.info "header #{header} found #{mail[ header.to_sym ]}" item_object.class.reflect_on_all_associations.map { |assoc| diff --git a/app/models/channel/filter/icinga.rb b/app/models/channel/filter/icinga.rb new file mode 100644 index 000000000..9bc48ffcc --- /dev/null +++ b/app/models/channel/filter/icinga.rb @@ -0,0 +1,77 @@ +# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ + +module Channel::Filter::Icinga + + def self.run(_channel, mail) + return if !Setting.get('ichinga_integration') + + # set config + integration = 'ichinga' + sender = Setting.get('ichinga_sender') + auto_close = Setting.get('ichinga_auto_close') + auto_close_state_id = Setting.get('ichinga_auto_close_state_id') + state_recovery_match = 'OK' + + return if !mail[:from] + return if !mail[:body] + sender_user_id = mail[ 'x-zammad-customer-id'.to_sym ] + return if !sender_user_id + + # check if sender is ichinga + return if !mail[:from].match(/#{sender}/i) + + # get mail attibutes like host and state + result = {} + mail[:body].gsub(%r{(Service|Host|State|Address|Date/Time|Additional\sInfo):(.+?)\n}i) { |_match| + key = $1 + if key + key = key.downcase + end + value = $2 + if value + value.strip! + end + result[key] = value + } + + # check if ticket with host is open + customer = User.lookup(id: sender_user_id) + + # follow up detection by meta data + open_states = Ticket::State.by_category('open') + Ticket.where(state: open_states).each {|ticket| + next if !ticket.preferences + next if !ticket.preferences['integration'] + next if ticket.preferences['integration'] != integration + next if !ticket.preferences['ichinga'] + next if !ticket.preferences['ichinga']['host'] + next if ticket.preferences['ichinga']['host'] != result['host'] + next if ticket.preferences['ichinga']['service'] != result['service'] + + # found open ticket for service+host + mail[ 'x-zammad-ticket-id'.to_sym ] = ticket.id + + # check if service is recovered + if auto_close && result['state'].match(/#{state_recovery_match}/i) + state = Ticket::State.lookup(id: auto_close_state_id) + if state + mail[ 'x-zammad-ticket-followup-state'.to_sym ] = state.name + end + end + return true + } + + # new ticket, set meta data + if !mail[ 'x-zammad-ticket-id'.to_sym ] + if !mail[ 'x-zammad-ticket-preferences'.to_sym ] + mail[ 'x-zammad-ticket-preferences'.to_sym ] = {} + end + preferences = {} + preferences['integration'] = integration + preferences['ichinga'] = result + preferences.each {|key, value| + mail[ 'x-zammad-ticket-preferences'.to_sym ][key] = value + } + end + end +end diff --git a/app/models/channel/filter/identify_sender.rb b/app/models/channel/filter/identify_sender.rb new file mode 100644 index 000000000..62cb8af6f --- /dev/null +++ b/app/models/channel/filter/identify_sender.rb @@ -0,0 +1,88 @@ +# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ + +module Channel::Filter::IdentifySender + + def self.run(_channel, mail) + + user_id = mail[ 'x-zammad-customer-id'.to_sym ] + user = nil + if user_id + user = User.lookup(id: user_id) + if !user + Rails.logger.debug "Invalid x-zammad-customer-id header '#{user_id}', no such user." + else + Rails.logger.debug "Took customer form x-zammad-customer-id header '#{user_id}'." + if user + create_recipients(mail) + return + end + end + end + + # check if sender exists in database + if mail[ 'x-zammad-customer-login'.to_sym ] + user = User.find_by(login: mail[ 'x-zammad-customer-login'.to_sym ]) + end + if !user + user = User.find_by(email: mail[ 'x-zammad-customer-email'.to_sym ] || mail[:from_email]) + end + if !user + user = user_create( + login: mail[ 'x-zammad-customer-login'.to_sym ] || mail[ 'x-zammad-customer-email'.to_sym ] || mail[:from_email], + firstname: mail[ 'x-zammad-customer-firstname'.to_sym ] || mail[:from_display_name], + lastname: mail[ 'x-zammad-customer-lastname'.to_sym ], + email: mail[ 'x-zammad-customer-email'.to_sym ] || mail[:from_email], + ) + end + + create_recipients(mail) + + mail[ 'x-zammad-customer-id'.to_sym ] = user.id + end + + # create to and cc user + def self.create_recipients(mail) + ['raw-to', 'raw-cc'].each { |item| + next if !mail[item.to_sym] + next if !mail[item.to_sym].addrs + items = mail[item.to_sym].addrs + items.each {|address_data| + user_create( + firstname: address_data.display_name, + lastname: '', + email: address_data.address, + ) + } + } + end + + def self.user_create(data) + + # return existing + user = User.find_by(login: data[:email].downcase) + return user if user + + # create new user + roles = Role.where(name: 'Customer') + + # fillup + %w(firstname lastname).each { |item| + if data[item.to_sym].nil? + data[item.to_sym] = '' + end + } + data[:password] = '' + data[:active] = true + data[:roles] = roles + data[:updated_by_id] = 1 + data[:created_by_id] = 1 + + user = User.create(data) + user.update_attributes( + updated_by_id: user.id, + created_by_id: user.id, + ) + user + end + +end diff --git a/db/migrate/20160412000002_add_ichinga_integration.rb b/db/migrate/20160412000002_add_ichinga_integration.rb new file mode 100644 index 000000000..c9934fea9 --- /dev/null +++ b/db/migrate/20160412000002_add_ichinga_integration.rb @@ -0,0 +1,152 @@ +class AddIchingaIntegration < ActiveRecord::Migration + def up + Setting.create_if_not_exists( + title: 'Define postmaster filter.', + name: '0015_postmaster_filter_identify_sender', + area: 'Postmaster::PreFilter', + description: 'Define postmaster filter to identify sender user.', + options: {}, + state: 'Channel::Filter::IdentifySender', + frontend: false + ) + Setting.create_if_not_exists( + title: 'Define postmaster filter.', + name: '0020_postmaster_filter_auto_response_check', + area: 'Postmaster::PreFilter', + description: 'Define postmaster filter to identify auto responses to prevent auto replies from Zammad.', + options: {}, + state: 'Channel::Filter::AutoResponseCheck', + frontend: false + ) + Setting.create_if_not_exists( + title: 'Define postmaster filter.', + name: '0030_postmaster_filter_out_of_office_check', + area: 'Postmaster::PreFilter', + description: 'Define postmaster filter to identify out of office emails for follow up detection and keeping current ticket state.', + options: {}, + state: 'Channel::Filter::OutOfOfficeCheck', + frontend: false + ) + Setting.create_if_not_exists( + title: 'Define postmaster filter.', + name: '0100_postmaster_filter_follow_up_check', + area: 'Postmaster::PreFilter', + description: 'Define postmaster filter to identify follow ups (based on admin settings).', + options: {}, + state: 'Channel::Filter::FollowUpCheck', + frontend: false + ) + Setting.create_if_not_exists( + title: 'Define postmaster filter.', + name: '0900_postmaster_filter_bounce_check', + area: 'Postmaster::PreFilter', + description: 'Define postmaster filter to identify postmaster bounced - to handle it as follow up of origin ticket.', + options: {}, + state: 'Channel::Filter::BounceCheck', + frontend: false + ) + Setting.create_if_not_exists( + title: 'Define postmaster filter.', + name: '1000_postmaster_filter_database_check', + area: 'Postmaster::PreFilter', + description: 'Define postmaster filter for filters managed via admin interface.', + options: {}, + state: 'Channel::Filter::Database', + frontend: false + ) + Setting.create_if_not_exists( + title: 'Define postmaster filter.', + name: '5000_postmaster_filter_ichinga', + area: 'Postmaster::PreFilter', + description: 'Define postmaster filter for manage Icinga (http://www.icinga.org) emails.', + options: {}, + state: 'Channel::Filter::Icinga', + frontend: false + ) + Setting.create_if_not_exists( + title: 'Icinga integration', + name: 'ichinga_integration', + area: 'Integration::Icinga', + description: 'Define if Icinga (http://www.icinga.org) is enabled or not.', + options: { + form: [ + { + display: '', + null: true, + name: 'ichinga_integration', + tag: 'boolean', + options: { + true => 'yes', + false => 'no', + }, + }, + ], + }, + state: false, + preferences: { prio: 1 }, + frontend: false + ) + Setting.create_if_not_exists( + title: 'Sender', + name: 'ichinga_sender', + area: 'Integration::Icinga', + description: 'Define the sender email address of Icinga emails.', + options: { + form: [ + { + display: '', + null: false, + name: 'ichinga_sender', + tag: 'input', + }, + ], + }, + state: 'icinga@monitoring.example.com', + frontend: false, + preferences: { prio: 2 }, + ) + Setting.create_if_not_exists( + title: 'Auto close', + name: 'ichinga_auto_close', + area: 'Integration::Icinga', + description: 'Define if tickets should be closed if service is recovered.', + options: { + form: [ + { + display: '', + null: true, + name: 'ichinga_auto_close', + tag: 'boolean', + options: { + true => 'yes', + false => 'no', + }, + }, + ], + }, + state: true, + preferences: { prio: 3 }, + frontend: false + ) + Setting.create_if_not_exists( + title: 'Auto close state', + name: 'ichinga_auto_close_state_id', + area: 'Integration::Icinga', + description: 'Define the ticket state of auto closed tickets.', + options: { + form: [ + { + display: '', + null: false, + name: 'ichinga_auto_close_state_id', + tag: 'select', + relation: 'TicketState', + }, + ], + }, + state: 4, + preferences: { prio: 4 }, + frontend: false + ) + end +end diff --git a/db/seeds.rb b/db/seeds.rb index f64085ad0..3f29b221a 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1525,6 +1525,15 @@ Setting.create_if_not_exists( state: 'Channel::Filter::Trusted', frontend: false ) +Setting.create_if_not_exists( + title: 'Define postmaster filter.', + name: '0015_postmaster_filter_identify_sender', + area: 'Postmaster::PreFilter', + description: 'Define postmaster filter to identify sender user.', + options: {}, + state: 'Channel::Filter::IdentifySender', + frontend: false +) Setting.create_if_not_exists( title: 'Define postmaster filter.', name: '0020_postmaster_filter_auto_response_check', @@ -1570,6 +1579,100 @@ Setting.create_if_not_exists( state: 'Channel::Filter::Database', frontend: false ) +Setting.create_if_not_exists( + title: 'Define postmaster filter.', + name: '5000_postmaster_filter_ichinga', + area: 'Postmaster::PreFilter', + description: 'Define postmaster filter for manage Icinga (http://www.icinga.org) emails.', + options: {}, + state: 'Channel::Filter::Icinga', + frontend: false +) +Setting.create_if_not_exists( + title: 'Icinga integration', + name: 'ichinga_integration', + area: 'Integration::Icinga', + description: 'Define if Icinga (http://www.icinga.org) is enabled or not.', + options: { + form: [ + { + display: '', + null: true, + name: 'ichinga_integration', + tag: 'boolean', + options: { + true => 'yes', + false => 'no', + }, + }, + ], + }, + state: false, + preferences: { prio: 1 }, + frontend: false +) +Setting.create_if_not_exists( + title: 'Sender', + name: 'ichinga_sender', + area: 'Integration::Icinga', + description: 'Define the sender email address of Icinga emails.', + options: { + form: [ + { + display: '', + null: false, + name: 'ichinga_sender', + tag: 'input', + }, + ], + }, + state: 'icinga@monitoring.example.com', + frontend: false, + preferences: { prio: 2 }, +) +Setting.create_if_not_exists( + title: 'Auto close', + name: 'ichinga_auto_close', + area: 'Integration::Icinga', + description: 'Define if tickets should be closed if service is recovered.', + options: { + form: [ + { + display: '', + null: true, + name: 'ichinga_auto_close', + tag: 'boolean', + options: { + true => 'yes', + false => 'no', + }, + }, + ], + }, + state: true, + preferences: { prio: 3 }, + frontend: false +) +Setting.create_if_not_exists( + title: 'Auto close state', + name: 'ichinga_auto_close_state_id', + area: 'Integration::Icinga', + description: 'Define the ticket state of auto closed tickets.', + options: { + form: [ + { + display: '', + null: false, + name: 'ichinga_auto_close_state_id', + tag: 'select', + relation: 'TicketState', + }, + ], + }, + state: 4, + preferences: { prio: 4 }, + frontend: false +) signature = Signature.create_if_not_exists( id: 1, diff --git a/test/unit/email_process_test.rb b/test/unit/email_process_test.rb index 4df8d6dae..cb0f82863 100644 --- a/test/unit/email_process_test.rb +++ b/test/unit/email_process_test.rb @@ -1996,6 +1996,7 @@ Some Text', data: 'From: me@example.com To: customer@example.com Subject: some subject +X-Zammad-Ticket-Followup-State: closed X-Zammad-Ticket-priority: 3 high X-Zammad-Article-sender: System x-Zammad-Article-type: phone @@ -2006,6 +2007,7 @@ Some Text', success: true, result: { 0 => { + state: 'new', priority: '3 high', title: 'some subject', }, @@ -2026,6 +2028,7 @@ Some Text', data: 'From: me@example.com To: customer@example.com Subject: some subject +X-Zammad-Ticket-Followup-State: closed X-Zammad-Ticket-Priority: 3 high X-Zammad-Article-Sender: System x-Zammad-Article-Type: phone @@ -2036,6 +2039,7 @@ Some Text', success: true, result: { 0 => { + state: 'new', priority: '2 normal', title: 'some subject', }, diff --git a/test/unit/integration_icinga_test.rb b/test/unit/integration_icinga_test.rb new file mode 100644 index 000000000..fa3ded1dc --- /dev/null +++ b/test/unit/integration_icinga_test.rb @@ -0,0 +1,193 @@ +# encoding: utf-8 +require 'test_helper' + +class IntegrationIcingaTest < ActiveSupport::TestCase + + test 'base tests' do + + Setting.set('ichinga_integration', true) + + # not matching sender + email_raw_string = "To: support@example.com +Subject: PROBLEM - host.internal.loc - CPU Load is WARNING +User-Agent: Heirloom mailx 12.5 7/5/10 +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: quoted-printable +Message-Id: <20160131094621.29ECD400F29C-1@monitoring.znuny.com> +From: icinga_not_matching@monitoring.example.com (icinga) + +***** Icinga ***** + +Notification Type: PROBLEM + +Service: CPU Load +Host: host.internal.loc +Address:=20 +State: WARNING + +Date/Time: 2016-01-31 10:46:20 +0100 + +Additional Info: WARNING - load average: 3.44, 0.99, 0.35 + +Comment: [] = +" + + ticket_p, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string) + assert_equal('new', ticket_p.state.name) + assert(ticket_p.preferences) + assert_not(ticket_p.preferences['integration']) + assert_not(ticket_p.preferences['ichinga']) + + # matching sender - CPU Load/host.internal.loc + email_raw_string = "To: support@example.com +Subject: PROBLEM - host.internal.loc - CPU Load is WARNING +User-Agent: Heirloom mailx 12.5 7/5/10 +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: quoted-printable +Message-Id: <20160131094621.29ECD400F29C-2@monitoring.znuny.com> +From: icinga@monitoring.example.com (icinga) + +***** Icinga ***** + +Notification Type: PROBLEM + +Service: CPU Load +Host: host.internal.loc +Address:=20 +State: WARNING + +Date/Time: 2016-01-31 10:46:20 +0100 + +Additional Info: WARNING - load average: 3.44, 0.99, 0.35 + +Comment: [] = +" + + ticket_1, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string) + assert_equal('new', ticket_1.state.name) + assert(ticket_1.preferences) + assert(ticket_1.preferences['integration']) + assert_equal('ichinga', ticket_1.preferences['integration']) + assert(ticket_1.preferences['ichinga']) + assert_equal('host.internal.loc', ticket_1.preferences['ichinga']['host']) + assert_equal('CPU Load', ticket_1.preferences['ichinga']['service']) + assert_equal('WARNING', ticket_1.preferences['ichinga']['state']) + + # matching sender - Disk Usage 123/host.internal.loc + email_raw_string = "To: support@example.com +Subject: PROBLEM - host.internal.loc - Disk Usage 123 is WARNING +User-Agent: Heirloom mailx 12.5 7/5/10 +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: quoted-printable +Message-Id: <20160131094621.29ECD400F29C-3@monitoring.znuny.com> +From: icinga@monitoring.example.com (icinga) + +***** Icinga ***** + +Notification Type: PROBLEM + +Service: Disk Usage 123 +Host: host.internal.loc +Address:=20 +State: WARNING + +Date/Time: 2016-01-31 10:46:20 +0100 + +Additional Info: WARNING - load average: 3.44, 0.99, 0.35 + +Comment: [] = +" + + ticket_2, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string) + assert_equal('new', ticket_2.state.name) + assert(ticket_2.preferences) + assert(ticket_2.preferences['integration']) + assert_equal('ichinga', ticket_2.preferences['integration']) + assert(ticket_2.preferences['ichinga']) + assert_equal('host.internal.loc', ticket_2.preferences['ichinga']['host']) + assert_equal('Disk Usage 123', ticket_2.preferences['ichinga']['service']) + assert_equal('WARNING', ticket_2.preferences['ichinga']['state']) + assert_not_equal(ticket_2.id, ticket_1.id) + + # matching sender - follow up - CPU Load/host.internal.loc + email_raw_string = "To: support@example.com +Subject: PROBLEM - host.internal.loc - CPU Load is WARNING +User-Agent: Heirloom mailx 12.5 7/5/10 +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: quoted-printable +Message-Id: <20160131094621.29ECD400F29C-4@monitoring.znuny.com> +From: icinga@monitoring.example.com (icinga) + +***** Icinga ***** + +Notification Type: PROBLEM + +Service: CPU Load +Host: host.internal.loc +Address:=20 +State: WARNING + +Date/Time: 2016-01-31 10:46:20 +0100 + +Additional Info: WARNING - load average: 3.44, 0.99, 0.35 + +Comment: [] = +" + + ticket_1_1, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string) + assert_equal('new', ticket_1_1.state.name) + assert(ticket_1_1.preferences) + assert(ticket_1_1.preferences['integration']) + assert_equal('ichinga', ticket_1_1.preferences['integration']) + assert(ticket_1_1.preferences['ichinga']) + assert_equal('host.internal.loc', ticket_1_1.preferences['ichinga']['host']) + assert_equal('CPU Load', ticket_1_1.preferences['ichinga']['service']) + assert_equal('WARNING', ticket_1_1.preferences['ichinga']['state']) + assert_equal(ticket_1.id, ticket_1_1.id) + + # matching sender - follow up - recovery - CPU Load/host.internal.loc + email_raw_string = "To: support@example.com +Subject: PROBLEM - host.internal.loc - CPU Load is WARNING +User-Agent: Heirloom mailx 12.5 7/5/10 +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: quoted-printable +Message-Id: <20160131094621.29ECD400F29C-5@monitoring.znuny.com> +From: icinga@monitoring.example.com (icinga) + +***** Icinga ***** + +Notification Type: RECOVERY + +Service: CPU Load +Host: host.internal.loc +Address:=20 +State: OK + +Date/Time: 2016-01-31 10:48:02 +0100 + +Additional Info: OK - load average: 1.62, 1.17, 0.49 + +Comment: [] = +" + + ticket_1_2, article_p, user_p, mail = Channel::EmailParser.new.process({}, email_raw_string) + assert_equal(ticket_1.id, ticket_1_2.id) + assert_equal('closed', ticket_1_2.state.name) + assert(ticket_1_2.preferences) + assert(ticket_1_2.preferences['integration']) + assert_equal('ichinga', ticket_1_2.preferences['integration']) + assert(ticket_1_2.preferences['ichinga']) + assert_equal('host.internal.loc', ticket_1_2.preferences['ichinga']['host']) + assert_equal('CPU Load', ticket_1_2.preferences['ichinga']['service']) + assert_equal('WARNING', ticket_1_2.preferences['ichinga']['state']) + + #Setting.set('ichinga_integration', false) + + end + +end