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