nagios-herald 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- MjBhMDg0MjY5NWYxMTk4NjMxOThjNjA5MzVkZWNmOTA4NGM2Y2FlYQ==
5
- data.tar.gz: !binary |-
6
- ZDU0MTJhZWNjZWU1Mjc2ODIyMjgwOGEyMjIwNDdiNzU0MmFmZDU5ZQ==
2
+ SHA1:
3
+ metadata.gz: 43674651e4996e822aaa24cb3b3ab308c78ed001
4
+ data.tar.gz: f99c755e90bb0095412a565c791961029dcfe1ea
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- OTRiNDA4MGRjMGYxZjk4NzBhZTJhZjcwZjRkZTQ2ZjAwMjNjMjA4ZTlhYjll
10
- MzZkY2ViODZjMDgwMGY4Nzg3YmQ2ZmNjNzMyNjE5MTlhNjRiNmM3NGFlNWIz
11
- NmNjYTZlZTI5ODJmZWZmOTY2NjU2NTQwNjkzOTY0MDYzOWZiMmE=
12
- data.tar.gz: !binary |-
13
- ZGViMzUwY2NiZTMyMDMxNmJmMWU3MTdjNmVmNGJlZjI0ODUxYjFlN2JkNTQ1
14
- ZDFkMzRkZjY0ZjEyY2EwMjUwOWI2ZjMzNGRlNGQ1MjMyNjA4M2YyNjRlMzQ4
15
- ZjUwNTRkZDJhOWE0ODhkMDBlMzJlOTI0OTVkYjk1NWM3Y2IxOGY=
6
+ metadata.gz: 5fb5a65b2acd8916aa04680cf4093bf70ecea27ba594cf63bc2e2ed451c2fccda2ab5f917281205f80391e56fbf39b97a8ac24d5ce04156b4fb2dd0d8634cfad
7
+ data.tar.gz: 2f3a7f725c30f69e8c2928c78230224a6d634330b9b37a40b18e107acda91599ce70313d4c0582a1d09062390482ddc1d7d4306ee9130a77a175f4e6c6342a98
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.0.4 (29th Oct, 2014)
2
+
3
+ Features:
4
+
5
+ - Support for short messages (i.e. SMS) added.
6
+
7
+ Thanks:
8
+ - @mrtazz is my hero.
9
+
1
10
  ## 0.0.3 (10th Oct, 2014)
2
11
 
3
12
  Features:
@@ -31,6 +31,7 @@ module NagiosHerald
31
31
  @content[:html]
32
32
  @content[:subject] = ""
33
33
  @content[:text]
34
+ @content[:short_text]
34
35
  @nagios_url = options[:nagios_url]
35
36
  @sandbox = get_sandbox_path
36
37
  @state_type = get_nagios_var("NAGIOS_SERVICESTATE") != "" ? "SERVICE" : "HOST"
@@ -57,6 +58,30 @@ module NagiosHerald
57
58
  formatters[subclass_base_name] = subclass
58
59
  end
59
60
 
61
+ # Public: remove a section from the content hash
62
+ #
63
+ # section - section to remove
64
+ #
65
+ # Returns the content that got removed or nil if the type or section
66
+ # doesn't exist
67
+ def delete_section_for_type(type, section)
68
+ return nil if @content[type].nil?
69
+ @content[type].delete(section)
70
+ end
71
+
72
+ def delete_html(section)
73
+ delete_section_for_type(:html, section)
74
+ end
75
+
76
+ def delete_text(section)
77
+ delete_section_for_type(:text, section)
78
+ end
79
+
80
+ def delete_short_text(section)
81
+ delete_section_for_type(:short_text, section)
82
+ end
83
+
84
+
60
85
  # Public: Concatenates text content.
61
86
  #
62
87
  # section - The content section name whose text we'll concatenate
@@ -77,6 +102,27 @@ module NagiosHerald
77
102
  end
78
103
  end
79
104
 
105
+ # Public: Concatenates short text content. This is really for usage in
106
+ # limited space messages like SMS or chat based mediums.
107
+ #
108
+ # section - The content section name whose text we'll concatenate
109
+ # text - The text we want to concatenate
110
+ #
111
+ # Example:
112
+ #
113
+ # add_short_text("state_detail", "Service is somewhere in Kansas")
114
+ #
115
+ # Returns the concatenated short text for the given section.
116
+ def add_short_text(section, text)
117
+ # Ensure our key is a symbol, regardless if we're passed a string or symbol
118
+ section = section.to_sym
119
+ if @content[:short_text][section].nil? or @content[:short_text][section].empty?
120
+ @content[:short_text][section] = text
121
+ else
122
+ @content[:short_text][section] += text
123
+ end
124
+ end
125
+
80
126
  # Public: Concatenates HTML content.
81
127
  #
82
128
  # section - The content section name whose HTML we'll concatenate
@@ -126,6 +172,7 @@ module NagiosHerald
126
172
  # Appends text and HTML output to the appropriate sections in @content
127
173
  def line_break(section)
128
174
  add_text(section, "\n")
175
+ add_short_text(section, "\n")
129
176
  add_html(section, "<br>")
130
177
  end
131
178
 
@@ -134,20 +181,23 @@ module NagiosHerald
134
181
  def host_info
135
182
  section = __method__
136
183
  text = ""
184
+ short_text = ""
137
185
  html = ""
138
- notification_type = get_nagios_var("NAGIOS_NOTIFICATIONTYPE")
139
186
  hostname = get_nagios_var("NAGIOS_HOSTNAME")
140
187
  service_desc = get_nagios_var("NAGIOS_SERVICEDESC")
141
188
  text += "Host: #{hostname} "
189
+ short_text += "#{hostname}"
142
190
  html += "<br><b>Host</b>: #{hostname} "
143
191
  if !service_desc.nil? and !service_desc.empty?
144
192
  text += "Service: #{service_desc}\n"
193
+ short_text += "/#{service_desc}\n"
145
194
  html += "<b>Service</b>: #{service_desc}<br/>"
146
195
  else
147
196
  # we need a trailing newline if no service description
148
197
  line_break(section)
149
198
  end
150
199
  add_text(section, text)
200
+ add_short_text(section, short_text)
151
201
  add_html(section, html)
152
202
  line_break(section)
153
203
  end
@@ -158,21 +208,24 @@ module NagiosHerald
158
208
  def state_info
159
209
  section = __method__
160
210
  text = ""
211
+ short_text = ""
161
212
  html = ""
162
213
  state = get_nagios_var("NAGIOS_#{@state_type}STATE")
163
214
  duration = get_nagios_var("NAGIOS_#{@state_type}DURATION")
164
- last_duration = get_nagios_var("NAGIOS_LAST#{@state_type}STATE")
215
+ last_state = get_nagios_var("NAGIOS_LAST#{@state_type}STATE")
165
216
  attempts = get_nagios_var("NAGIOS_#{@state_type}ATTEMPT")
166
217
  max_attempts = get_nagios_var("NAGIOS_MAX#{@state_type}ATTEMPTS")
167
218
 
168
- text += "State is now: #{state} for #{duration} (was #{last_duration}) after #{attempts} / #{max_attempts} checks\n"
219
+ text += "State is now: #{state} for #{duration} (was #{last_state}) after #{attempts} / #{max_attempts} checks\n"
220
+ short_text += "#{state} for #{duration} (was #{last_state}) after #{attempts} / #{max_attempts} checks\n"
169
221
 
170
222
  if state.eql? 'OK' or state.eql? 'UP'
171
- html += "State is now: <b>#{state}</b> for <b>#{duration}</b> (was #{last_duration}) after <b>#{attempts} / #{max_attempts}</b> checks<br/>"
223
+ html += "State is now: <b>#{state}</b> for <b>#{duration}</b> (was #{last_state}) after <b>#{attempts} / #{max_attempts}</b> checks<br/>"
172
224
  else
173
- html += "State is now: <b><font style='color:red'>#{state}</font></b> for <b>#{duration}</b> (was #{last_duration}) after <b>#{attempts} / #{max_attempts}</b> checks<br/>"
225
+ html += "State is now: <b><font style='color:red'>#{state}</font></b> for <b>#{duration}</b> (was #{last_state}) after <b>#{attempts} / #{max_attempts}</b> checks<br/>"
174
226
  end
175
227
  add_text(section, text)
228
+ add_short_text(section, short_text)
176
229
  add_html(section, html)
177
230
  line_break(section)
178
231
  end
@@ -197,12 +250,15 @@ module NagiosHerald
197
250
  def additional_info
198
251
  section = __method__
199
252
  text = ""
253
+ short_text = ""
200
254
  html = ""
201
255
  output = get_nagios_var("NAGIOS_#{@state_type}OUTPUT")
202
256
  if !output.nil? and !output.empty?
203
257
  text += "Additional Info: #{unescape_text(output)}\n\n"
258
+ short_text += "#{unescape_text(output)}\n\n"
204
259
  html += "<b>Additional Info</b>: #{output}<br><br>"
205
260
  add_text(section, text)
261
+ add_short_text(section, short_text)
206
262
  add_html(section, html)
207
263
  end
208
264
  end
@@ -212,12 +268,15 @@ module NagiosHerald
212
268
  def additional_details
213
269
  section = __method__
214
270
  text = ""
271
+ short_text = ""
215
272
  html = ""
216
273
  long_output = get_nagios_var("NAGIOS_LONG#{@state_type}OUTPUT")
217
274
  if !long_output.nil? and !long_output.empty?
218
275
  text += "Additional Details: #{unescape_text(long_output)}\n"
276
+ short_text += "#{unescape_text(long_output)}\n"
219
277
  html += "<b>Additional Details</b>: <pre>#{unescape_text(long_output)}</pre><br><br>"
220
278
  add_text(section, text)
279
+ add_short_text(section, short_text)
221
280
  add_html(section, html)
222
281
  end
223
282
  end
@@ -278,7 +337,6 @@ module NagiosHerald
278
337
  html = ""
279
338
  recipients = get_nagios_var("NAGIOS_NOTIFICATIONRECIPIENTS")
280
339
  return if recipients.nil?
281
- recipients_list = recipients.split(',')
282
340
  text += "Sent to #{recipients}\n\n"
283
341
  html += "Sent to #{recipients}<br><br>"
284
342
  add_text(section, text)
@@ -290,27 +348,33 @@ module NagiosHerald
290
348
  def ack_info
291
349
  section = __method__
292
350
  text = ""
351
+ short_text = ""
293
352
  html = ""
294
353
  date = get_nagios_var("NAGIOS_LONGDATETIME")
295
354
  author = get_nagios_var("NAGIOS_#{@state_type}ACKAUTHOR")
296
355
  comment = get_nagios_var("NAGIOS_#{@state_type}ACKCOMMENT")
297
356
  hostname = get_nagios_var("NAGIOS_HOSTNAME")
298
357
 
299
- text += "At #{date} #{author}"
300
- html += "At #{date} #{author}"
358
+ text += "At #{date} #{author}"
359
+ short_text += "At #{date} #{author}"
360
+ html += "At #{date} #{author}"
301
361
 
302
362
  if @state_type == "SERVICE"
303
363
  desc = get_nagios_var("NAGIOS_SERVICEDESC")
304
- text += " acknowledged #{hostname}/#{desc}.\n\n"
305
- html += " acknowledged #{hostname}/#{desc}.<br><br>"
364
+ text += " acknowledged #{hostname}/#{desc}.\n\n"
365
+ short_text += " acked #{hostname}/#{desc}.\n\n"
366
+ html += " acknowledged #{hostname}/#{desc}.<br><br>"
306
367
  else
307
- text += " acknowledged #{hostname}.\n\n"
308
- html += " acknowledged #{hostname}.<br><br>"
368
+ text += " acknowledged #{hostname}.\n\n"
369
+ short_text += " acked #{hostname}.\n\n"
370
+ html += " acknowledged #{hostname}.<br><br>"
309
371
 
310
372
  end
311
- text += "Comment: #{comment}" if comment
312
- html += "Comment: #{comment}" if comment
373
+ text += "Comment: #{comment}" if comment
374
+ short_text += "#{comment}" if comment
375
+ html += "Comment: #{comment}" if comment
313
376
  add_text(section, text)
377
+ add_short_text(section, short_text)
314
378
  add_html(section, html)
315
379
  end
316
380
 
@@ -473,7 +537,7 @@ module NagiosHerald
473
537
  else
474
538
  logger.fatal "Invalid Nagios notification type! Expecting something like PROBLEM or RECOVERY. We got #{nagios_notification_type}."
475
539
  exit 1
476
- end
540
+ end
477
541
  end
478
542
 
479
543
  # Public: Generates a subject.
@@ -489,11 +553,19 @@ module NagiosHerald
489
553
  subject += "/#{service_desc}" if service_desc != ""
490
554
 
491
555
  if @state_type == "SERVICE"
492
- subject="#{notification_type} Service #{subject} is #{state}"
556
+ long_subject="#{notification_type} Service #{subject} is #{state}"
493
557
  else
494
- subject="#{notification_type} Host #{subject} is #{state}"
558
+ long_subject="#{notification_type} Host #{subject} is #{state}"
495
559
  end
496
- @content[:subject] = subject
560
+
561
+ if notification_type == "ACKNOWLEDGEMENT"
562
+ short_subject="ACK: #{subject} is #{state}"
563
+ else
564
+ short_subject="#{subject} is #{state}"
565
+ end
566
+
567
+ @content[:subject] = long_subject
568
+ @content[:short_subject] = short_subject
497
569
  end
498
570
 
499
571
  # Public: Generates content body.
@@ -64,8 +64,9 @@ module NagiosHerald
64
64
  # Returns nothing. Updates the formatter content hash.
65
65
  def additional_info
66
66
  section = __method__
67
+ super
68
+ delete_html(section)
67
69
  output = get_nagios_var("NAGIOS_#{@state_type}OUTPUT")
68
- add_text(section, "Additional Info:\n #{unescape_text(output)}\n\n") if output
69
70
 
70
71
  # Collect partitions data and plot a chart
71
72
  # if the check has recovered, $NAGIOS_SERVICEOUTPUT doesn't contain the data we need to parse for images; just give us the A-OK message
@@ -15,7 +15,7 @@ module NagiosHerald
15
15
  # recipients - A list of recipients for this message.
16
16
  # options - The options hash from Executor.
17
17
  # FIXME: Is that ^^ necessary now with Config.config available?
18
- #
18
+ #
19
19
  # Returns a new Message::Email object.
20
20
  def initialize(recipients, options = {})
21
21
  @replyto = options[:replyto]
@@ -96,12 +96,12 @@ module NagiosHerald
96
96
  def build_message
97
97
  curate_text
98
98
  curate_html
99
+ @subject = self.content[:subject]
99
100
  if @no_send
100
101
  self.print
101
102
  return
102
103
  end
103
104
 
104
- @subject = self.content[:subject]
105
105
  mail = Mail.new({
106
106
  :from => @replyto,
107
107
  :to => @recipients,
@@ -142,7 +142,7 @@ module NagiosHerald
142
142
 
143
143
  def send
144
144
  mail = self.build_message
145
- mail.deliver!
145
+ mail.deliver! unless mail.nil?
146
146
  end
147
147
 
148
148
  end
@@ -28,9 +28,11 @@ module NagiosHerald
28
28
  def curate_text
29
29
  notification_type = get_nagios_var('NAGIOS_NOTIFICATIONTYPE')
30
30
  if notification_type.eql?('ACKNOWLEDGEMENT')
31
- @text += self.content[:text][:ack_info]
31
+ @text += self.content[:short_text][:ack_info] unless self.content[:short_text][:ack_info].empty?
32
32
  else
33
- @text += self.content[:text][:additional_info]
33
+ [:state_info, :additional_info, :additional_details].each do |info|
34
+ @text += self.content[:short_text][info] unless self.content[:short_text][info].empty?
35
+ end
34
36
  end
35
37
  end
36
38
 
@@ -39,9 +41,7 @@ module NagiosHerald
39
41
  #
40
42
  # Returns nothing.
41
43
  def print
42
- puts "------------------"
43
- puts "Subject : #{@subject}"
44
- puts "------------------"
44
+ puts @subject
45
45
  puts @text
46
46
  end
47
47
 
@@ -49,24 +49,29 @@ module NagiosHerald
49
49
  #
50
50
  # Returns nothing.
51
51
  def build_message
52
+ @subject = self.content[:short_subject]
52
53
  curate_text
53
- if @no_send
54
- self.print
55
- return
56
- end
57
54
 
58
- @subject = self.content[:subject]
55
+
59
56
  mail = Mail.new({
60
57
  :from => @replyto,
61
58
  :to => @recipients,
62
59
  :subject => @subject,
63
60
  :body => @text
64
61
  })
62
+
63
+ if @no_send
64
+ self.print
65
+ return
66
+ end
67
+
68
+
69
+ return mail
65
70
  end
66
71
 
67
72
  def send
68
- self.build_message
69
- mail.deliver!
73
+ mail = self.build_message
74
+ mail.deliver! unless mail.nil?
70
75
  end
71
76
 
72
77
  end
@@ -1,3 +1,3 @@
1
1
  module NagiosHerald
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -39,6 +39,17 @@ class TestMessagePager < MiniTest::Unit::TestCase
39
39
  :recipients_email_link=>"Sent to ops\n\n",
40
40
  :notification_info=>"Notification sent at: Thu May 16 21:06:38 UTC 2013 (notification number 1)\n\n",
41
41
  :alert_ack_url=>"Acknowledge this alert: http://nagios.example.com/nagios/cgi-bin/cmd.cgi?cmd_typ=34&host=web.example.com&service=Disk%20Space\nAlternatively, reply to this message with the word 'ack' in the body to acknowledge the alert.\n"
42
+ },
43
+ :short_text => {
44
+ :host_info=>"web.example.com/Disk Space\n\n",
45
+ :state_info=>"CRITICAL for 0d 0h 5m 12s (was CRITICAL) after 3 / 3 checks\n\n",
46
+ :additional_info=>"DISK CRITICAL - free space: / 7002 MB (18% inode 60%): /data 16273093 MB (26% inode 99%):\n\n",
47
+ :action_url=>"Action URL: http://runbook.example.com/disk_space_alerts.html\n\n",
48
+ :notes=>"",
49
+ :additional_details=>"THRESHOLDS - WARNING:15%;CRITICAL:20%;\n\nFilesystem Size Used Avail Use% Mounted on\n/dev/vda 40G 31G 6.9G 82% /\ntmpfs 2.5G 83M 2.4G 4% /dev/shm\nnfs.example.example.com:/mnt/user/homes\n 59T 43T 16T 74% /data\n\n",
50
+ :recipients_email_link=>"Sent to ops\n\n",
51
+ :notification_info=>"Notification sent at: Thu May 16 21:06:38 UTC 2013 (notification number 1)\n\n",
52
+ :alert_ack_url=>"Acknowledge this alert: http://nagios.example.com/nagios/cgi-bin/cmd.cgi?cmd_typ=34&host=web.example.com&service=Disk%20Space\nAlternatively, reply to this message with the word 'ack' in the body to acknowledge the alert.\n"
42
53
  }
43
54
  }
44
55
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nagios-herald
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Frantz
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-10-10 00:00:00.000000000 Z
12
+ date: 2014-10-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: app_conf
@@ -18,7 +18,7 @@ dependencies:
18
18
  - - ~>
19
19
  - !ruby/object:Gem::Version
20
20
  version: '0.4'
21
- - - ! '>='
21
+ - - '>='
22
22
  - !ruby/object:Gem::Version
23
23
  version: 0.4.2
24
24
  type: :runtime
@@ -28,7 +28,7 @@ dependencies:
28
28
  - - ~>
29
29
  - !ruby/object:Gem::Version
30
30
  version: '0.4'
31
- - - ! '>='
31
+ - - '>='
32
32
  - !ruby/object:Gem::Version
33
33
  version: 0.4.2
34
34
  - !ruby/object:Gem::Dependency
@@ -38,7 +38,7 @@ dependencies:
38
38
  - - ~>
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.1'
41
- - - ! '>='
41
+ - - '>='
42
42
  - !ruby/object:Gem::Version
43
43
  version: 0.1.6
44
44
  type: :runtime
@@ -48,7 +48,7 @@ dependencies:
48
48
  - - ~>
49
49
  - !ruby/object:Gem::Version
50
50
  version: '0.1'
51
- - - ! '>='
51
+ - - '>='
52
52
  - !ruby/object:Gem::Version
53
53
  version: 0.1.6
54
54
  - !ruby/object:Gem::Dependency
@@ -58,7 +58,7 @@ dependencies:
58
58
  - - ~>
59
59
  - !ruby/object:Gem::Version
60
60
  version: '2.5'
61
- - - ! '>='
61
+ - - '>='
62
62
  - !ruby/object:Gem::Version
63
63
  version: 2.5.4
64
64
  type: :runtime
@@ -68,40 +68,41 @@ dependencies:
68
68
  - - ~>
69
69
  - !ruby/object:Gem::Version
70
70
  version: '2.5'
71
- - - ! '>='
71
+ - - '>='
72
72
  - !ruby/object:Gem::Version
73
73
  version: 2.5.4
74
74
  - !ruby/object:Gem::Dependency
75
75
  name: chef
76
76
  requirement: !ruby/object:Gem::Requirement
77
77
  requirements:
78
- - - ! '>='
78
+ - - '>='
79
79
  - !ruby/object:Gem::Version
80
80
  version: 11.8.2
81
81
  type: :runtime
82
82
  prerelease: false
83
83
  version_requirements: !ruby/object:Gem::Requirement
84
84
  requirements:
85
- - - ! '>='
85
+ - - '>='
86
86
  - !ruby/object:Gem::Version
87
87
  version: 11.8.2
88
88
  - !ruby/object:Gem::Dependency
89
89
  name: elasticsearch
90
90
  requirement: !ruby/object:Gem::Requirement
91
91
  requirements:
92
- - - ! '>='
92
+ - - '>='
93
93
  - !ruby/object:Gem::Version
94
94
  version: 1.0.2
95
95
  type: :runtime
96
96
  prerelease: false
97
97
  version_requirements: !ruby/object:Gem::Requirement
98
98
  requirements:
99
- - - ! '>='
99
+ - - '>='
100
100
  - !ruby/object:Gem::Version
101
101
  version: 1.0.2
102
- description: ! " A project that aims to make it easy to provide context in Nagios
103
- alerts.\n The project consists of a core notifier script that can be called with
104
- a formatter\n to tailor the content of the message sent to an operator.\n"
102
+ description: |2
103
+ A project that aims to make it easy to provide context in Nagios alerts.
104
+ The project consists of a core notifier script that can be called with a formatter
105
+ to tailor the content of the message sent to an operator.
105
106
  email:
106
107
  - rfrantz@etsy.com
107
108
  - nkammah@etsy.com
@@ -211,19 +212,18 @@ require_paths:
211
212
  - lib
212
213
  required_ruby_version: !ruby/object:Gem::Requirement
213
214
  requirements:
214
- - - ! '>='
215
+ - - '>='
215
216
  - !ruby/object:Gem::Version
216
217
  version: 1.9.2
217
218
  required_rubygems_version: !ruby/object:Gem::Requirement
218
219
  requirements:
219
- - - ! '>='
220
+ - - '>='
220
221
  - !ruby/object:Gem::Version
221
222
  version: '0'
222
223
  requirements: []
223
224
  rubyforge_project:
224
- rubygems_version: 2.2.2
225
+ rubygems_version: 2.0.14
225
226
  signing_key:
226
227
  specification_version: 4
227
228
  summary: A project that aims to make it easy to provide context in Nagios alerts.
228
229
  test_files: []
229
- has_rdoc: