Added icinga integration.
This commit is contained in:
parent
1fd36cfc6a
commit
f48e742d23
12 changed files with 692 additions and 74 deletions
|
@ -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')
|
|
@ -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')
|
|
@ -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')
|
|
@ -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')
|
|
@ -62,9 +62,8 @@ class Ticket extends App.ControllerTabs
|
||||||
]
|
]
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
App.Config.set( 'SettingBranding', { prio: 1200, parent: '#settings', name: 'Branding', target: '#settings/branding', controller: Branding, 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('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('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('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('SettingImport', { prio: 1800, parent: '#settings', name: 'Import', target: '#settings/import', controller: Import, role: ['Admin'] }, 'NavBarAdmin')
|
||||||
|
|
||||||
|
|
|
@ -43,12 +43,18 @@ class Channel::EmailParser
|
||||||
x-zammad-customer-firstname: '',
|
x-zammad-customer-firstname: '',
|
||||||
x-zammad-customer-lastname: '',
|
x-zammad-customer-lastname: '',
|
||||||
|
|
||||||
# ticket headers
|
# ticket headers (for new tickets)
|
||||||
x-zammad-ticket-group: 'some_group',
|
x-zammad-ticket-group: 'some_group',
|
||||||
x-zammad-ticket-state: 'some_state',
|
x-zammad-ticket-state: 'some_state',
|
||||||
x-zammad-ticket-priority: 'some_priority',
|
x-zammad-ticket-priority: 'some_priority',
|
||||||
x-zammad-ticket-owner: 'some_owner_login',
|
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
|
# article headers
|
||||||
x-zammad-article-internal: false,
|
x-zammad-article-internal: false,
|
||||||
x-zammad-article-type: 'agent',
|
x-zammad-article-type: 'agent',
|
||||||
|
@ -66,7 +72,6 @@ class Channel::EmailParser
|
||||||
|
|
||||||
# set all headers
|
# set all headers
|
||||||
mail.header.fields.each { |field|
|
mail.header.fields.each { |field|
|
||||||
|
|
||||||
next if !field.name
|
next if !field.name
|
||||||
|
|
||||||
# full line, encode, ready for storage
|
# full line, encode, ready for storage
|
||||||
|
@ -79,20 +84,15 @@ class Channel::EmailParser
|
||||||
# get sender
|
# get sender
|
||||||
from = nil
|
from = nil
|
||||||
['from', 'reply-to', 'return-path'].each { |item|
|
['from', 'reply-to', 'return-path'].each { |item|
|
||||||
|
|
||||||
next if !mail[ item.to_sym ]
|
next if !mail[ item.to_sym ]
|
||||||
|
|
||||||
from = mail[ item.to_sym ].value
|
from = mail[ item.to_sym ].value
|
||||||
|
|
||||||
break if from
|
break if from
|
||||||
}
|
}
|
||||||
|
|
||||||
# set x-any-recipient
|
# set x-any-recipient
|
||||||
data['x-any-recipient'.to_sym] = ''
|
data['x-any-recipient'.to_sym] = ''
|
||||||
['to', 'cc', 'delivered-to', 'x-original-to', 'envelope-to'].each { |item|
|
['to', 'cc', 'delivered-to', 'x-original-to', 'envelope-to'].each { |item|
|
||||||
|
|
||||||
next if !mail[item.to_sym]
|
next if !mail[item.to_sym]
|
||||||
|
|
||||||
if data['x-any-recipient'.to_sym] != ''
|
if data['x-any-recipient'.to_sym] != ''
|
||||||
data['x-any-recipient'.to_sym] += ', '
|
data['x-any-recipient'.to_sym] += ', '
|
||||||
end
|
end
|
||||||
|
@ -347,7 +347,7 @@ retrns
|
||||||
|
|
||||||
# run postmaster pre filter
|
# run postmaster pre filter
|
||||||
filters = {}
|
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[setting.name] = Kernel.const_get(Setting.get(setting.name))
|
||||||
}
|
}
|
||||||
filters.each {|_prio, backend|
|
filters.each {|_prio, backend|
|
||||||
|
@ -373,35 +373,15 @@ retrns
|
||||||
# reset current_user
|
# reset current_user
|
||||||
UserInfo.current_user_id = 1
|
UserInfo.current_user_id = 1
|
||||||
|
|
||||||
# create sender
|
# create sender if needed
|
||||||
if mail[ 'x-zammad-customer-login'.to_sym ]
|
sender_user_id = mail[ 'x-zammad-customer-id'.to_sym ]
|
||||||
user = User.find_by(login: mail[ 'x-zammad-customer-login'.to_sym ])
|
if !sender_user_id
|
||||||
|
raise 'No x-zammad-customer-id, no sender set!'
|
||||||
end
|
end
|
||||||
|
user = User.lookup(id: sender_user_id)
|
||||||
if !user
|
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
|
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
|
# set current user
|
||||||
UserInfo.current_user_id = user.id
|
UserInfo.current_user_id = user.id
|
||||||
|
@ -416,6 +396,8 @@ retrns
|
||||||
|
|
||||||
# set ticket state to open if not new
|
# set ticket state to open if not new
|
||||||
if ticket
|
if ticket
|
||||||
|
set_attributes_by_x_headers(ticket, 'ticket', mail, 'followup')
|
||||||
|
|
||||||
state = Ticket::State.find(ticket.state_id)
|
state = Ticket::State.find(ticket.state_id)
|
||||||
state_type = Ticket::StateType.find(state.state_type_id)
|
state_type = Ticket::StateType.find(state.state_type_id)
|
||||||
|
|
||||||
|
@ -425,9 +407,11 @@ retrns
|
||||||
end
|
end
|
||||||
|
|
||||||
# set ticket to open again
|
# set ticket to open again
|
||||||
if state_type.name != 'new' && !mail[ 'x-zammad-out-of-office'.to_sym ]
|
if !mail[ 'x-zammad-ticket-followup-state'.to_sym ]
|
||||||
ticket.state = Ticket::State.find_by(name: 'open')
|
if state_type.name != 'new' && !mail[ 'x-zammad-out-of-office'.to_sym ]
|
||||||
ticket.save
|
ticket.state = Ticket::State.find_by(name: 'open')
|
||||||
|
ticket.save
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -505,7 +489,7 @@ retrns
|
||||||
Observer::Ticket::Notification.transaction
|
Observer::Ticket::Notification.transaction
|
||||||
|
|
||||||
# run postmaster post filter
|
# 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[setting.name] = Kernel.const_get(Setting.get(setting.name))
|
||||||
}
|
}
|
||||||
filters.each {|_prio, backend|
|
filters.each {|_prio, backend|
|
||||||
|
@ -521,36 +505,7 @@ retrns
|
||||||
[ticket, article, user, mail]
|
[ticket, article, user, mail]
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_create(data)
|
def set_attributes_by_x_headers(item_object, header_name, mail, suffix = false)
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# loop all x-zammad-hedaer-* headers
|
# loop all x-zammad-hedaer-* headers
|
||||||
item_object.attributes.each {|key, _value|
|
item_object.attributes.each {|key, _value|
|
||||||
|
@ -566,6 +521,9 @@ retrns
|
||||||
if key_short == '_id'
|
if key_short == '_id'
|
||||||
key_short = key[ 0, key.length - 3 ]
|
key_short = key[ 0, key.length - 3 ]
|
||||||
header = "x-zammad-#{header_name}-#{key_short}"
|
header = "x-zammad-#{header_name}-#{key_short}"
|
||||||
|
if suffix
|
||||||
|
header = "x-zammad-#{header_name}-#{suffix}-#{key_short}"
|
||||||
|
end
|
||||||
if mail[ header.to_sym ]
|
if mail[ header.to_sym ]
|
||||||
Rails.logger.info "header #{header} found #{mail[ header.to_sym ]}"
|
Rails.logger.info "header #{header} found #{mail[ header.to_sym ]}"
|
||||||
item_object.class.reflect_on_all_associations.map { |assoc|
|
item_object.class.reflect_on_all_associations.map { |assoc|
|
||||||
|
|
77
app/models/channel/filter/icinga.rb
Normal file
77
app/models/channel/filter/icinga.rb
Normal file
|
@ -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
|
88
app/models/channel/filter/identify_sender.rb
Normal file
88
app/models/channel/filter/identify_sender.rb
Normal file
|
@ -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
|
152
db/migrate/20160412000002_add_ichinga_integration.rb
Normal file
152
db/migrate/20160412000002_add_ichinga_integration.rb
Normal file
|
@ -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
|
103
db/seeds.rb
103
db/seeds.rb
|
@ -1525,6 +1525,15 @@ Setting.create_if_not_exists(
|
||||||
state: 'Channel::Filter::Trusted',
|
state: 'Channel::Filter::Trusted',
|
||||||
frontend: false
|
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(
|
Setting.create_if_not_exists(
|
||||||
title: 'Define postmaster filter.',
|
title: 'Define postmaster filter.',
|
||||||
name: '0020_postmaster_filter_auto_response_check',
|
name: '0020_postmaster_filter_auto_response_check',
|
||||||
|
@ -1570,6 +1579,100 @@ Setting.create_if_not_exists(
|
||||||
state: 'Channel::Filter::Database',
|
state: 'Channel::Filter::Database',
|
||||||
frontend: false
|
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(
|
signature = Signature.create_if_not_exists(
|
||||||
id: 1,
|
id: 1,
|
||||||
|
|
|
@ -1996,6 +1996,7 @@ Some Text',
|
||||||
data: 'From: me@example.com
|
data: 'From: me@example.com
|
||||||
To: customer@example.com
|
To: customer@example.com
|
||||||
Subject: some subject
|
Subject: some subject
|
||||||
|
X-Zammad-Ticket-Followup-State: closed
|
||||||
X-Zammad-Ticket-priority: 3 high
|
X-Zammad-Ticket-priority: 3 high
|
||||||
X-Zammad-Article-sender: System
|
X-Zammad-Article-sender: System
|
||||||
x-Zammad-Article-type: phone
|
x-Zammad-Article-type: phone
|
||||||
|
@ -2006,6 +2007,7 @@ Some Text',
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
0 => {
|
0 => {
|
||||||
|
state: 'new',
|
||||||
priority: '3 high',
|
priority: '3 high',
|
||||||
title: 'some subject',
|
title: 'some subject',
|
||||||
},
|
},
|
||||||
|
@ -2026,6 +2028,7 @@ Some Text',
|
||||||
data: 'From: me@example.com
|
data: 'From: me@example.com
|
||||||
To: customer@example.com
|
To: customer@example.com
|
||||||
Subject: some subject
|
Subject: some subject
|
||||||
|
X-Zammad-Ticket-Followup-State: closed
|
||||||
X-Zammad-Ticket-Priority: 3 high
|
X-Zammad-Ticket-Priority: 3 high
|
||||||
X-Zammad-Article-Sender: System
|
X-Zammad-Article-Sender: System
|
||||||
x-Zammad-Article-Type: phone
|
x-Zammad-Article-Type: phone
|
||||||
|
@ -2036,6 +2039,7 @@ Some Text',
|
||||||
success: true,
|
success: true,
|
||||||
result: {
|
result: {
|
||||||
0 => {
|
0 => {
|
||||||
|
state: 'new',
|
||||||
priority: '2 normal',
|
priority: '2 normal',
|
||||||
title: 'some subject',
|
title: 'some subject',
|
||||||
},
|
},
|
||||||
|
|
193
test/unit/integration_icinga_test.rb
Normal file
193
test/unit/integration_icinga_test.rb
Normal file
|
@ -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
|
Loading…
Reference in a new issue