1+ # encoding: utf-8
2+ require "logstash/outputs/base"
3+ require "logstash/namespace"
4+ require 'json'
5+ require "uri"
6+ require "net/http"
7+ require "net/https"
8+
9+ # The Jira Service Management output is used to Create, Close, Acknowledge Alerts and Add Note to alerts in Jira Service Management.
10+ # For this output to work, your event must contain "jsmAction" field and you must configure apiKey field in configuration.
11+ # If jsmAction is "create", event must contain "message" field.
12+ # For other actions ("close", "acknowledge" or "note"), event must contain "alias" or "alertId" field.
13+ #
14+ # If your event have the following fields (If you use default field names).
15+ #
16+ # Example event:
17+ #
18+ # {
19+ # "note" => "test note",
20+ # "jsmAction" => "create",
21+ # "teams" => ["teams"],
22+ # "description" => "test description",
23+ # "source" => "test source",
24+ # "message" => "test message",
25+ # "priority" => "P4",
26+ # "tags" => ["tags"],
27+ # "@timestamp" => 2017-09-15T13:32:00.747Z,
28+ # "@version" => "1",
29+ # "host" => "Neo's-MacBook-Pro.local",
30+ # "alias" => "test-alias",
31+ # "details" => {
32+ # "prop2" => "val2",
33+ # "prop1" => "val1"
34+ # },
35+ # "actions" => ["actions"],
36+ # "user" => "test user",
37+ # "entity" => "test entity"
38+ # }
39+ #
40+ # An alert with following properties will be created.
41+ #
42+ # {
43+ # "message": "test message",
44+ # "alias": "test alias",
45+ # "teams": ["teams"],
46+ # "description": "test description",
47+ # "source": "test source",
48+ # "note": "test note",
49+ # "user": "test user",
50+ # "priority": "P4",
51+ # "tags": [
52+ # "tags"
53+ # ],
54+ # "details": {
55+ # "prop2": "val2",
56+ # "prop1": "val1"
57+ # },
58+ # "actions": [
59+ # "actions"
60+ # ],
61+ # "entity": "test entity",
62+ # }
63+ #
64+ # Fields with prefix "Attribute" are the keys of the fields will be extracted from Logstash event.
65+
66+ class LogStash ::Outputs ::Jsm < LogStash ::Outputs ::Base
67+
68+ config_name "jsm"
69+
70+ # Jira Service Management Logstash Integration API Key
71+ config :apiKey , :validate => :string , :required => true
72+
73+ # Proxy settings
74+ config :proxy_address , :validate => :string , :required => false
75+ config :proxy_port , :validate => :number , :required => false
76+
77+
78+ # Host of Jira Service Management api, normally you should not need to change this field.
79+ config :jsmBaseUrl , :validate => :string , :required => false , :default => 'https://api.atlassian.com/jsm/ops/integration/v2/alerts/'
80+
81+ # Url will be used to close alerts in Jira Service Management
82+ config :closeActionPath , :validate => :string , :required => false , :default => '/close'
83+
84+ # Url will be used to acknowledge alerts in Jira Service Management
85+ config :acknowledgeActionPath , :validate => :string , :required => false , :default => '/acknowledge'
86+
87+ # Url will be used to add notes to alerts in Jira Service Management
88+ config :noteActionPath , :validate => :string , :required => false , :default => '/notes'
89+
90+ # The value of this field holds the name of the action will be executed in Jira Service Management.
91+ # This field must be in Event object. Should be one of "create", "close", "acknowledge" or "note". Other values will be discarded.
92+ config :actionAttribute , :validate => :string , :required => false , :default => 'jsmAction'
93+
94+ # This value specifies the query parameter identifierType
95+ config :identifierType , :validate => :string , :required => false , :default => 'id'
96+
97+ # This value will be set to eventual identifier according to event(id/alias).
98+ config :identifier , :validate => :string , :required => false , :default => ''
99+
100+ # The value of this field holds the Id of the alert that actions will be executed.
101+ # One of "alertId" or "alias" field must be in Event object, except from "create" action
102+ config :alertIdAttribute , :validate => :string , :required => false , :default => 'alertId'
103+
104+ # The value of this field holds the alias of the alert that actions will be executed.
105+ # One of "alertId" or "alias" field must be in Event object, except from "create" action
106+ config :aliasAttribute , :validate => :string , :required => false , :default => 'alias'
107+
108+ # The value of this field holds the alert text.
109+ config :messageAttribute , :validate => :string , :required => false , :default => 'message'
110+
111+ # The value of this field holds the list of team names which will be responsible for the alert.
112+ config :teamsAttribute , :validate => :string , :required => false , :default => 'teams'
113+
114+ # The value of this field holds the Teams and users that the alert will become
115+ # visible to without sending any notification.
116+ config :visibleToAttribute , :validate => :string , :required => false , :default => 'visibleTo'
117+
118+ # The value of this field holds the detailed description of the alert.
119+ config :descriptionAttribute , :validate => :string , :required => false , :default => 'description'
120+
121+ # The value of this field holds the comma separated list of actions that can be executed on the alert.
122+ config :actionsAttribute , :validate => :string , :required => false , :default => 'actions'
123+
124+ # The value of this field holds the source of alert. By default, it will be assigned to IP address of incoming request.
125+ config :sourceAttribute , :validate => :string , :required => false , :default => 'source'
126+
127+ # The value of this field holds the priority level of the alert
128+ config :priorityAttribute , :validate => :string , :required => false , :default => 'priority'
129+
130+ # The value of this field holds the comma separated list of labels attached to the alert.
131+ config :tagsAttribute , :validate => :string , :required => false , :default => 'tags'
132+
133+ # The value of this field holds the set of user defined properties. This will be specified as a nested JSON map
134+ config :detailsAttribute , :validate => :string , :required => false , :default => 'details'
135+
136+ # The value of this field holds the entity the alert is related to.
137+ config :entityAttribute , :validate => :string , :required => false , :default => 'entity'
138+
139+ # The value of this field holds the default owner of the execution. If user is not specified, owner of account will be used.
140+ config :userAttribute , :validate => :string , :required => false , :default => 'user'
141+
142+ # The value of this field holds the additional alert note.
143+ config :noteAttribute , :validate => :string , :required => false , :default => 'note'
144+
145+
146+ public
147+ def register
148+ end # def register
149+
150+ public
151+ def populateAliasOrId ( event , params )
152+ alertAlias = event . get ( @aliasAttribute ) if event . get ( @aliasAttribute )
153+ if alertAlias == nil then
154+ alertId = event . get ( @alertIdAttribute ) if event . get ( @alertIdAttribute )
155+ if !( alertId == nil ) then
156+ @identifierType = 'id'
157+ @identifier = alertId
158+ end
159+ else
160+ @identifierType = 'alias'
161+ @identifier = alertAlias
162+ end
163+ end # def populateAliasOrId
164+
165+ public
166+ def executePost ( uri , params )
167+ unless uri == nil then
168+ @logger . info ( "Executing url #{ uri } " )
169+ url = URI ( uri )
170+ http = Net ::HTTP . new ( url . host , url . port , @proxy_address , @proxy_port )
171+ if url . scheme == 'https'
172+ http . use_ssl = true
173+ http . verify_mode = OpenSSL ::SSL ::VERIFY_NONE
174+ end
175+ request = Net ::HTTP ::Post . new ( url . request_uri , initheader = { "Content-Type" => "application/json" , "Authorization" => "GenieKey #{ @apiKey } " } )
176+ request . body = params . to_json
177+ response = http . request ( request )
178+ body = response . body
179+ body = JSON . parse ( body )
180+ @logger . warn ( "Executed [#{ uri } ]. Response:[#{ body } ]" )
181+ end
182+ end # def executePost
183+
184+ public
185+ def receive ( event )
186+ return unless output? ( event )
187+
188+ @logger . info ( "processing #{ event } " )
189+ jsmAction = event . get ( @actionAttribute ) if event . get ( @actionAttribute )
190+ if jsmAction then
191+ params = { }
192+ populateCommonContent ( params , event )
193+
194+ case jsmAction . downcase
195+ when "create"
196+ uri = "#{ @jsmBaseUrl } "
197+ params = populateCreateAlertContent ( params , event )
198+ when "close"
199+ uri = "#{ @jsmBaseUrl } #{ @identifier } #{ @closeActionPath } ?identifierType=#{ @identifierType } "
200+ when "acknowledge"
201+ uri = "#{ @jsmBaseUrl } #{ @identifier } #{ @acknowledgeActionPath } ?identifierType=#{ @identifierType } "
202+ when "note"
203+ uri = "#{ @jsmBaseUrl } #{ @identifier } #{ @noteActionPath } ?identifierType=#{ @identifierType } "
204+ else
205+ @logger . warn ( "Action #{ jsmAction } does not match any available action, discarding.." )
206+ return
207+ end
208+
209+ executePost ( uri , params )
210+ else
211+ @logger . warn ( "No jsmAction defined" )
212+ return
213+ end
214+ end # def receive
215+
216+ private
217+ def populateCreateAlertContent ( params , event )
218+ params [ 'message' ] = event . get ( @messageAttribute ) if event . get ( @messageAttribute )
219+ params [ 'alias' ] = event . get ( @aliasAttribute ) if event . get ( @aliasAttribute )
220+ params [ 'teams' ] = event . get ( @teamsAttribute ) if event . get ( @teamsAttribute )
221+ params [ 'visibleTo' ] = event . get ( @visibleToAttribute ) if event . get ( @visibleToAttribute )
222+ params [ 'description' ] = event . get ( @descriptionAttribute ) if event . get ( @descriptionAttribute )
223+ params [ 'actions' ] = event . get ( @actionsAttribute ) if event . get ( @actionsAttribute )
224+ params [ 'tags' ] = event . get ( @tagsAttribute ) if event . get ( @tagsAttribute )
225+ params [ 'entity' ] = event . get ( @entityAttribute ) if event . get ( @entityAttribute )
226+ params [ 'priority' ] = event . get ( @priorityAttribute ) if event . get ( @priorityAttribute )
227+ params [ 'details' ] = event . get ( @detailsAttribute ) if event . get ( @detailsAttribute )
228+
229+
230+ return params
231+ end
232+
233+ private
234+ def populateCommonContent ( params , event )
235+ populateAliasOrId ( event , params )
236+ params [ 'source' ] = event . get ( @sourceAttribute ) if event . get ( @sourceAttribute )
237+ params [ 'user' ] = event . get ( @userAttribute ) if event . get ( @userAttribute )
238+ params [ 'note' ] = event . get ( @noteAttribute ) if event . get ( @noteAttribute )
239+ end
240+
241+ end # class LogStash::Outputs::Jsm
0 commit comments