logstash-output-jsm 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6ada91aab7f1f4d99dadc5dfddb192898e6737ea26a52106a5b960c99d882c32
4
+ data.tar.gz: f3c344ac7a36c52cd6d72c64715dcad00ca9994c2908f4739c890ec3b8be789b
5
+ SHA512:
6
+ metadata.gz: beb0ef29d50f43c9578a878f68818ce9c9120ecebdb3bb83215810963e17b5c4dbe250e1c88622226815440ec6539f9c61864463f08da868761c4242f6c12da8
7
+ data.tar.gz: ec8c72410a84a2c04acfef641644824583c3d12888e13996b0c60fb669daa613ef37f089b4690a941e563a8318e7505000e1e043db826a93a0fa8cf8a66a0636
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/NOTICE.TXT ADDED
@@ -0,0 +1,5 @@
1
+ Elasticsearch
2
+ Copyright 2012-2015 Elasticsearch
3
+
4
+ This product includes software developed by The Apache Software
5
+ Foundation (http://www.apache.org/).
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Jira Service Management Logstash Plugin
2
+
3
+ 1. Download the [Logstash plugin](https://github.com/elastic/logstash).<br/>
4
+ The plugin is entirely free and open-source. It's under the Apache 2.0 license, so you can use it in the way that
5
+ best suits your needs.
6
+
7
+ 2. Install and run the Jira Service Management output plugin in Logstash:
8
+ * For Logstash 5.4+ <br/>
9
+ `bin/logstash-plugin install logstash-output-jsm`
10
+ * For other versions <br/>
11
+ `bin/plugin install logstash-output-jsm`
12
+
13
+ 3. Add a Logstash integration in Jira Service Management and copy the API key. <br/>
14
+ :warning: If the feature isn’t available on your site, keep checking Jira Service Management for updates.
15
+
16
+ 4. Use plugins such as [Mutate](https://www.elastic.co/guide/en/logstash/current/plugins-filters-mutate.html) to populate the fields that [logstash-output-jsm](https://github.com/atlassian/jsm-integration-scripts/) will use.
17
+
18
+ ``` ruby
19
+ filter {
20
+ mutate {
21
+ add_field => {
22
+ "jsmAction" => "create"
23
+ "alias" => "neo123"
24
+ "description" => "Every alert needs a description"
25
+ "actions" => ["Restart", "AnExampleAction"]
26
+ "tags" => ["OverwriteQuietHours","Critical"]
27
+ "[details][prop1]" => "val1"
28
+ "[details][prop2]" => "val2"
29
+ "entity" => "An example entity"
30
+ "priority" => "P4"
31
+ "source" => "custom source"
32
+ "user" => "custom user"
33
+ "note" => "alert is created"
34
+ }
35
+ }
36
+ ruby {
37
+ code => "event.set('teams', [{'name' => 'Integration'}, {'name' => 'Platform'}])"
38
+ }
39
+ }
40
+ ```
41
+
42
+ 5. Add the following to your configuration file and enter the integration API key you copied earlier into **apiKey**.
43
+ ``` ruby
44
+ output {
45
+ jsm {
46
+ "apiKey" => "logstash_integration_api_key"
47
+ }
48
+ }
49
+ ```
50
+
51
+ 6. Run Logstash.
@@ -0,0 +1,241 @@
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,0 +1,24 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-output-jsm'
3
+ s.version = '0.1.1'
4
+ s.licenses = ["Apache License (2.0)"]
5
+ s.summary = "This output Creates, Closes, Acknowledges alerts and Adds Note to alerts in Jira Service Management."
6
+ s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
7
+ s.authors = ["Elastic"]
8
+ s.email = "info@elastic.co"
9
+ s.homepage = "http://www.elastic.co/guide/en/logstash/current/index.html"
10
+ s.require_paths = ["lib"]
11
+
12
+ # Files
13
+ s.files = Dir['lib/**/*', 'spec/**/*', 'vendor/**/*', '*.gemspec', '*.md', 'CONTRIBUTORS', 'Gemfile', 'LICENSE', 'NOTICE.TXT']
14
+ # Tests
15
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
+
17
+ # Special flag to let us know this is actually a logstash plugin
18
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output", "github_repo" => "https://github.com/atlassian/jsm-integration-scripts/tree/master/logstash-plugin" }
19
+
20
+ # Gem dependencies
21
+ s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
22
+ s.add_runtime_dependency "logstash-codec-plain"
23
+ s.add_development_dependency "logstash-devutils"
24
+ end
@@ -0,0 +1,52 @@
1
+ require "logstash/devutils/rspec/spec_helper"
2
+ require "logstash/outputs/jsm"
3
+ require "logstash/codecs/plain"
4
+ require "logstash/event"
5
+
6
+ describe LogStash::Outputs::Jsm do
7
+
8
+ subject {LogStash::Outputs::Jsm.new("apiKey" => "my_api_key" )}
9
+ let(:logger) { subject.logger}
10
+
11
+ describe "receive message" do
12
+
13
+ it "when jsmAction is not specified" do
14
+ expect(logger).to receive(:warn).with("No JSM action defined").once
15
+ subject.receive({"message" => "test_alert","@version" => "1","@timestamp" => "2015-09-22T11:20:00.250Z"})
16
+ end
17
+
18
+ it "when jsmAction is not valid" do
19
+ action = "invalid"
20
+ expect(logger).to receive(:warn).with("Action #{action} does not match any available action, discarding..").once
21
+ subject.receive({"message" => "test_alert","@version" => "1","@timestamp" => "2015-09-22T11:20:00.250Z", "jsmAction" => action})
22
+ end
23
+
24
+ it "when jsmAction is 'create'" do
25
+ event = {"message" => "test_alert", "@version" => "1", "@timestamp" => "2015-09-22T11:20:00.250Z", "jsmAction" => "create"}
26
+ expect(logger).to receive(:info).with("processing #{event}").once
27
+ expect(logger).to receive(:info).with("Executing url #{subject.jsmBaseUrl}#{subject.createActionUrl}").once
28
+ subject.receive(event)
29
+ end
30
+
31
+ it "when jsmAction is 'close'" do
32
+ event = {"message" => "test_alert", "@version" => "1", "@timestamp" => "2015-09-22T11:20:00.250Z", "jsmAction" => "close"}
33
+ expect(logger).to receive(:info).with("processing #{event}").once
34
+ expect(logger).to receive(:info).with("Executing url #{subject.jsmBaseUrl}#{subject.closeActionUrl}").once
35
+ subject.receive(event)
36
+ end
37
+
38
+ it "when jsmAction is 'acknowledge'" do
39
+ event = {"message" => "test_alert", "@version" => "1", "@timestamp" => "2015-09-22T11:20:00.250Z", "jsmAction" => "acknowledge"}
40
+ expect(logger).to receive(:info).with("processing #{event}").once
41
+ expect(logger).to receive(:info).with("Executing url #{subject.jsmBaseUrl}#{subject.acknowledgeActionUrl}").once
42
+ subject.receive(event)
43
+ end
44
+
45
+ it "when jsmAction is 'note'" do
46
+ event = {"message" => "test_alert", "@version" => "1", "@timestamp" => "2015-09-22T11:20:00.250Z", "jsmAction" => "note"}
47
+ expect(logger).to receive(:info).with("processing #{event}").once
48
+ expect(logger).to receive(:info).with("Executing url #{subject.jsmBaseUrl}#{subject.noteActionUrl}").once
49
+ subject.receive(event)
50
+ end
51
+ end
52
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-output-jsm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Elastic
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-12-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logstash-core-plugin-api
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '1.60'
20
+ - - "<="
21
+ - !ruby/object:Gem::Version
22
+ version: '2.99'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.60'
30
+ - - "<="
31
+ - !ruby/object:Gem::Version
32
+ version: '2.99'
33
+ - !ruby/object:Gem::Dependency
34
+ name: logstash-codec-plain
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: logstash-devutils
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ description: This gem is a logstash plugin required to be installed on top of the
62
+ Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This
63
+ gem is not a stand-alone program
64
+ email: info@elastic.co
65
+ executables: []
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - Gemfile
70
+ - NOTICE.TXT
71
+ - README.md
72
+ - lib/logstash/outputs/jsm.rb
73
+ - logstash-output-jsm.gemspec
74
+ - spec/outputs/jsm_spec.rb
75
+ homepage: http://www.elastic.co/guide/en/logstash/current/index.html
76
+ licenses:
77
+ - Apache License (2.0)
78
+ metadata:
79
+ logstash_plugin: 'true'
80
+ logstash_group: output
81
+ github_repo: https://github.com/atlassian/jsm-integration-scripts/tree/master/logstash-plugin
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.3.5
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: This output Creates, Closes, Acknowledges alerts and Adds Note to alerts
101
+ in Jira Service Management.
102
+ test_files:
103
+ - spec/outputs/jsm_spec.rb