snmp_table_viewer 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,296 @@
1
+ module SNMPTableViewer
2
+
3
+ # Converter for the interface table.
4
+ # Replaces type with "number (meaning)"
5
+ # Replaces admin state with "number (meaning)"
6
+ # Replaces oper state with "number (meaning)"
7
+ # Replaces last change with "Timeticks: (number) D days, H:M:S.ms"
8
+ class Converter::IfTable < Converter
9
+ def self.convert(data)
10
+ admin_states = ['up', 'down', 'testing']
11
+ oper_states = ['up', 'down', 'testing', 'unknown', 'dormant', 'notPresent', 'lowerLayerDown']
12
+ types = {
13
+ 1 => 'other',
14
+ 2 => 'regular1822',
15
+ 3 => 'hdh1822',
16
+ 4 => 'ddnX25',
17
+ 5 => 'rfc877x25',
18
+ 6 => 'ethernetCsmacd',
19
+ 7 => 'iso88023Csmacd',
20
+ 8 => 'iso88024TokenBus',
21
+ 9 => 'iso88025TokenRing',
22
+ 10 => 'iso88026Man',
23
+ 11 => 'starLan',
24
+ 12 => 'proteon10Mbit',
25
+ 13 => 'proteon80Mbit',
26
+ 14 => 'hyperchannel',
27
+ 15 => 'fddi',
28
+ 16 => 'lapb',
29
+ 17 => 'sdlc',
30
+ 18 => 'ds1',
31
+ 19 => 'e1',
32
+ 20 => 'basicISDN',
33
+ 21 => 'primaryISDN',
34
+ 22 => 'propPointToPointSerial',
35
+ 23 => 'ppp',
36
+ 24 => 'softwareLoopback',
37
+ 25 => 'eon',
38
+ 26 => 'ethernet3Mbit',
39
+ 27 => 'nsip',
40
+ 28 => 'slip',
41
+ 29 => 'ultra',
42
+ 30 => 'ds3',
43
+ 31 => 'sip',
44
+ 32 => 'frameRelay',
45
+ 33 => 'rs232',
46
+ 34 => 'para',
47
+ 35 => 'arcnet',
48
+ 36 => 'arcnetPlus',
49
+ 37 => 'atm',
50
+ 38 => 'miox25',
51
+ 39 => 'sonet',
52
+ 40 => 'x25ple',
53
+ 41 => 'iso88022llc',
54
+ 42 => 'localTalk',
55
+ 43 => 'smdsDxi',
56
+ 44 => 'frameRelayService',
57
+ 45 => 'v35',
58
+ 46 => 'hssi',
59
+ 47 => 'hippi',
60
+ 48 => 'modem',
61
+ 49 => 'aal5',
62
+ 50 => 'sonetPath',
63
+ 51 => 'sonetVT',
64
+ 52 => 'smdsIcip',
65
+ 53 => 'propVirtual',
66
+ 54 => 'propMultiplexor',
67
+ 55 => 'ieee80212',
68
+ 56 => 'fibreChannel',
69
+ 57 => 'hippiInterface',
70
+ 58 => 'frameRelayInterconnect',
71
+ 59 => 'aflane8023',
72
+ 60 => 'aflane8025',
73
+ 61 => 'cctEmul',
74
+ 62 => 'fastEther',
75
+ 63 => 'isdn',
76
+ 64 => 'v11',
77
+ 65 => 'v36',
78
+ 66 => 'g703at64k',
79
+ 67 => 'g703at2mb',
80
+ 68 => 'qllc',
81
+ 69 => 'fastEtherFX',
82
+ 70 => 'channel',
83
+ 71 => 'ieee80211',
84
+ 72 => 'ibm370parChan',
85
+ 73 => 'escon',
86
+ 74 => 'dlsw',
87
+ 75 => 'isdns',
88
+ 76 => 'isdnu',
89
+ 77 => 'lapd',
90
+ 78 => 'ipSwitch',
91
+ 79 => 'rsrb',
92
+ 80 => 'atmLogical',
93
+ 81 => 'ds0',
94
+ 82 => 'ds0Bundle',
95
+ 83 => 'bsc',
96
+ 84 => 'async',
97
+ 85 => 'cnr',
98
+ 86 => 'iso88025Dtr',
99
+ 87 => 'eplrs',
100
+ 88 => 'arap',
101
+ 89 => 'propCnls',
102
+ 90 => 'hostPad',
103
+ 91 => 'termPad',
104
+ 92 => 'frameRelayMPI',
105
+ 93 => 'x213',
106
+ 94 => 'adsl',
107
+ 95 => 'radsl',
108
+ 96 => 'sdsl',
109
+ 97 => 'vdsl',
110
+ 98 => 'iso88025CRFPInt',
111
+ 99 => 'myrinet',
112
+ 100 => 'voiceEM',
113
+ 101 => 'voiceFXO',
114
+ 102 => 'voiceFXS',
115
+ 103 => 'voiceEncap',
116
+ 104 => 'voiceOverIp',
117
+ 105 => 'atmDxi',
118
+ 106 => 'atmFuni',
119
+ 107 => 'atmIma',
120
+ 108 => 'pppMultilinkBundle',
121
+ 109 => 'ipOverCdlc',
122
+ 110 => 'ipOverClaw',
123
+ 111 => 'stackToStack',
124
+ 112 => 'virtualIpAddress',
125
+ 113 => 'mpc',
126
+ 114 => 'ipOverAtm',
127
+ 115 => 'iso88025Fiber',
128
+ 116 => 'tdlc',
129
+ 117 => 'gigabitEthernet',
130
+ 118 => 'hdlc',
131
+ 119 => 'lapf',
132
+ 120 => 'v37',
133
+ 121 => 'x25mlp',
134
+ 122 => 'x25huntGroup',
135
+ 123 => 'transpHdlc',
136
+ 124 => 'interleave',
137
+ 125 => 'fast',
138
+ 126 => 'ip',
139
+ 127 => 'docsCableMaclayer',
140
+ 128 => 'docsCableDownstream',
141
+ 129 => 'docsCableUpstream',
142
+ 130 => 'a12MppSwitch',
143
+ 131 => 'tunnel',
144
+ 132 => 'coffee',
145
+ 133 => 'ces',
146
+ 134 => 'atmSubInterface',
147
+ 135 => 'l2vlan',
148
+ 136 => 'l3ipvlan',
149
+ 137 => 'l3ipxvlan',
150
+ 138 => 'digitalPowerline',
151
+ 139 => 'mediaMailOverIp',
152
+ 140 => 'dtm',
153
+ 141 => 'dcn',
154
+ 142 => 'ipForward',
155
+ 143 => 'msdsl',
156
+ 144 => 'ieee1394',
157
+ 145 => 'if-gsn',
158
+ 146 => 'dvbRccMacLayer',
159
+ 147 => 'dvbRccDownstream',
160
+ 148 => 'dvbRccUpstream',
161
+ 149 => 'atmVirtual',
162
+ 150 => 'mplsTunnel',
163
+ 151 => 'srp',
164
+ 152 => 'voiceOverAtm',
165
+ 153 => 'voiceOverFrameRelay',
166
+ 154 => 'idsl',
167
+ 155 => 'compositeLink',
168
+ 156 => 'ss7SigLink',
169
+ 157 => 'propWirelessP2P',
170
+ 158 => 'frForward',
171
+ 159 => 'rfc1483',
172
+ 160 => 'usb',
173
+ 161 => 'ieee8023adLag',
174
+ 162 => 'bgppolicyaccounting',
175
+ 163 => 'frf16MfrBundle',
176
+ 164 => 'h323Gatekeeper',
177
+ 165 => 'h323Proxy',
178
+ 166 => 'mpls',
179
+ 167 => 'mfSigLink',
180
+ 168 => 'hdsl2',
181
+ 169 => 'shdsl',
182
+ 170 => 'ds1FDL',
183
+ 171 => 'pos',
184
+ 172 => 'dvbAsiIn',
185
+ 173 => 'dvbAsiOut',
186
+ 174 => 'plc',
187
+ 175 => 'nfas',
188
+ 176 => 'tr008',
189
+ 177 => 'gr303RDT',
190
+ 178 => 'gr303IDT',
191
+ 179 => 'isup',
192
+ 180 => 'propDocsWirelessMaclayer',
193
+ 181 => 'propDocsWirelessDownstream',
194
+ 182 => 'propDocsWirelessUpstream',
195
+ 183 => 'hiperlan2',
196
+ 184 => 'propBWAp2Mp',
197
+ 185 => 'sonetOverheadChannel',
198
+ 186 => 'digitalWrapperOverheadChannel',
199
+ 187 => 'aal2',
200
+ 188 => 'radioMAC',
201
+ 189 => 'atmRadio',
202
+ 190 => 'imt',
203
+ 191 => 'mvl',
204
+ 192 => 'reachDSL',
205
+ 193 => 'frDlciEndPt',
206
+ 194 => 'atmVciEndPt',
207
+ 195 => 'opticalChannel',
208
+ 196 => 'opticalTransport',
209
+ 197 => 'propAtm',
210
+ 198 => 'voiceOverCable',
211
+ 199 => 'infiniband',
212
+ 200 => 'teLink',
213
+ 201 => 'q2931',
214
+ 202 => 'virtualTg',
215
+ 203 => 'sipTg',
216
+ 204 => 'sipSig',
217
+ 205 => 'docsCableUpstreamChannel',
218
+ 206 => 'econet',
219
+ 207 => 'pon155',
220
+ 208 => 'pon622',
221
+ 209 => 'bridge',
222
+ 210 => 'linegroup',
223
+ 211 => 'voiceEMFGD',
224
+ 212 => 'voiceFGDEANA',
225
+ 213 => 'voiceDID',
226
+ 214 => 'mpegTransport',
227
+ 215 => 'sixToFour',
228
+ 216 => 'gtp',
229
+ 217 => 'pdnEtherLoop1',
230
+ 218 => 'pdnEtherLoop2',
231
+ 219 => 'opticalChannelGroup',
232
+ 220 => 'homepna',
233
+ 221 => 'gfp',
234
+ 222 => 'ciscoISLvlan',
235
+ 223 => 'actelisMetaLOOP',
236
+ 224 => 'fcipLink',
237
+ 225 => 'rpr',
238
+ 226 => 'qam',
239
+ 227 => 'lmp',
240
+ 228 => 'cblVectaStar',
241
+ 229 => 'docsCableMCmtsDownstream',
242
+ 230 => 'adsl2',
243
+ 231 => 'macSecControlledIF',
244
+ 232 => 'macSecUncontrolledIF',
245
+ 233 => 'aviciOpticalEther',
246
+ 234 => 'atmbond',
247
+ 235 => 'voiceFGDOS',
248
+ 236 => 'mocaVersion1',
249
+ 237 => 'ieee80216WMAN',
250
+ 238 => 'adsl2plus',
251
+ 239 => 'dvbRcsMacLayer',
252
+ 240 => 'dvbTdm',
253
+ 241 => 'dvbRcsTdma',
254
+ 242 => 'x86Laps',
255
+ 243 => 'wwanPP',
256
+ 244 => 'wwanPP2',
257
+ 245 => 'voiceEBS',
258
+ 246 => 'ifPwType',
259
+ 247 => 'ilan',
260
+ 248 => 'pip',
261
+ 249 => 'aluELP',
262
+ 250 => 'gpon',
263
+ 251 => 'vdsl2',
264
+ 252 => 'capwapDot11Profile',
265
+ 253 => 'capwapDot11Bss',
266
+ 254 => 'capwapWtpVirtualRadio',
267
+ 255 => 'bits',
268
+ 256 => 'docsCableUpstreamRfPort',
269
+ 257 => 'cableDownstreamRfPort',
270
+ 258 => 'vmwareVirtualNic',
271
+ 259 => 'ieee802154',
272
+ 260 => 'otnOdu',
273
+ 261 => 'otnOtu',
274
+ 262 => 'ifVfiType',
275
+ 263 => 'g9981',
276
+ 264 => 'g9982',
277
+ 265 => 'g9983',
278
+ 266 => 'aluEpon',
279
+ 267 => 'aluEponOnu',
280
+ 268 => 'aluEponPhysicalUni',
281
+ 269 => 'aluEponLogicalLink',
282
+ 270 => 'aluGponOnu',
283
+ 271 => 'aluGponPhysicalUni',
284
+ 272 => 'vmwareNicTeam',
285
+ }
286
+ data.map do |item|
287
+ item[2] = "#{item[2]} (#{types[item[2]]})"
288
+ item[6] = "#{item[6]} (#{admin_states[item[6]-1]})"
289
+ item[7] = "#{item[7]} (#{oper_states[item[7]-1]})"
290
+ item[8] = item[8].to_s # Timeticks for last change
291
+ item
292
+ end
293
+ end
294
+ end # IfTable Converter
295
+
296
+ end # module SNMPTableViewer
@@ -0,0 +1,8 @@
1
+ module SNMPTableViewer
2
+
3
+ # Parent class for converters. Which take data and perform sometranslations/conversions to it..
4
+ # @abstract
5
+ class Converter
6
+ end # Converter base class
7
+
8
+ end # module SNMPTableViewer
@@ -0,0 +1,24 @@
1
+ module SNMPTableViewer
2
+
3
+ # Class to fetch the information from an SNMP host.
4
+ class Fetcher
5
+
6
+ # Fetch the data using SNMP.
7
+ # @param base_oid [String] The OID to start the SNMP walk from
8
+ # @param **snmp_options [Hash] The options to pass to NETSNMP::Client.new
9
+ # @return [Array<Array<#to_s>>] A two dimensional array containing objects in each cell (at 'address' data[row][col])
10
+ def self.from_snmp(base_oid:, **snmp_options)
11
+ data = Array.new
12
+ NETSNMP::Client.new(snmp_options) do |manager|
13
+ manager.walk(oid: base_oid).each do |oid, value|
14
+ col, row = oid.split('.')[-2..-1].map{ |i| i.to_i - 1}
15
+ data[row] ||= []
16
+ data[row][col] = value
17
+ end
18
+ end
19
+ data
20
+ end
21
+
22
+ end # class Fetcher
23
+
24
+ end # module SNMPTableViewer
@@ -0,0 +1,12 @@
1
+ module SNMPTableViewer
2
+
3
+ # Formatting class for CSV output.
4
+ class Formatter::CSV < Formatter
5
+ # Output the data (and headings if provided).
6
+ # @return [String] the CSV data
7
+ def output()
8
+ data_with_headings.map(&:to_csv).join
9
+ end
10
+ end # CSV Formatter
11
+
12
+ end # module SNMPTableViewer
@@ -0,0 +1,17 @@
1
+ module SNMPTableViewer
2
+
3
+ # Formatting class for JSON output
4
+ class Formatter::JSON < Formatter
5
+ # Output the data. If no headings are provided you'll get an Array or Arrays. If headings are provided you'll get an Array of Hashes.
6
+ # @return [String] the JSON data
7
+ def output()
8
+ data = @data
9
+ if @headings.size > 0
10
+ # Convert inner arrays to hashes
11
+ data = data.map{ |row| Hash[row.map.with_index{ |value, index| [@headings[index] || '', value] }] }
12
+ end
13
+ ::JSON.generate(data)
14
+ end
15
+ end # JSON Formatter
16
+
17
+ end # module SNMPTableViewer
@@ -0,0 +1,12 @@
1
+ module SNMPTableViewer
2
+
3
+ # Formatting class for raw output.
4
+ class Formatter::Raw < Formatter
5
+ # Output the data (and headings if provided).
6
+ # @return [String] the raw data
7
+ def output()
8
+ data_with_headings.inspect
9
+ end
10
+ end # Raw Fromatter
11
+
12
+ end # module SNMPTableViewer
@@ -0,0 +1,30 @@
1
+ module SNMPTableViewer
2
+
3
+ # Formatting class for Table output.
4
+ class Formatter::Table < Formatter
5
+ # Output the data (and headings if provided) as a table which can be displayed in the terminal.
6
+ # @param transpose [Boolean] Whether to swap rows and columns - useful for displaying a wide and short table
7
+ # @return [String] the text to display in order to draw the table
8
+ def output(transpose: false)
9
+ use_headings = @headings.size > 0
10
+ data = @data
11
+
12
+ if transpose
13
+ # transpose the data
14
+ if use_headings
15
+ data = [@headings, *data].transpose
16
+ use_headings = false # Don't add headings twice
17
+ else
18
+ data = data.transpose
19
+ end
20
+ end
21
+
22
+ if use_headings
23
+ Terminal::Table.new(rows: data, headings: @headings).to_s
24
+ else
25
+ Terminal::Table.new(rows: data).to_s
26
+ end
27
+ end
28
+ end # Table Formatter
29
+
30
+ end # module SNMPTableViewer
@@ -0,0 +1,30 @@
1
+ module SNMPTableViewer
2
+
3
+ # Parent class for formatters. Which take data and headings and output it in a given format.
4
+ # @abstract
5
+ class Formatter
6
+
7
+ # Create a new instance of Formatter.
8
+ # @param data [Array<Array<#to_s>>] A two dimensional array containing objects in each cell (at 'address' data[row][col])
9
+ # @param headings [Array<String, #to_s>] An array of headings for each column
10
+ # @return [Formatter]
11
+ def initialize(data:, headings: [])
12
+ @headings = headings
13
+ @data = data
14
+ end
15
+
16
+ private
17
+ # Get a two dimensioan array of headings prepended to the data or just the data (if no headings were provided).
18
+ # @return Array<Array<#to_s>>
19
+ def data_with_headings
20
+ has_headings? ? [@headings, *@data] : @data
21
+ end
22
+ # Determine if headings were passed.
23
+ # @return [Boolean]
24
+ def has_headings?
25
+ @headings.is_a?(Array) && @headings.size > 0
26
+ end
27
+
28
+ end # Formatter base class
29
+
30
+ end # module SNMPTableViewer
@@ -0,0 +1,14 @@
1
+ # Require gems
2
+ require 'netsnmp'
3
+ require 'terminal-table'
4
+
5
+ # Require from standard ruby libraries
6
+ require 'csv'
7
+ require 'json'
8
+
9
+ # Load the other files in the lib directory
10
+ require_relative '../version'
11
+ Dir["#{File.dirname(__FILE__)}/**/*.rb"].select{ |f| f != __FILE__}.sort.each { |f| load(f) }
12
+
13
+ module SNMPTableViewer
14
+ end # module SNMPTableViewer
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require File.join(File.dirname(__FILE__), 'version')
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "snmp_table_viewer"
7
+ s.license = 'BSD 3 clause'
8
+ s.version = SNMPTableViewer::VERSION
9
+ s.authors = ['Robert Gauld']
10
+ s.email = ['robert@robertgauld.co.uk']
11
+ s.homepage = 'https://github.com/robertgauld/snmp-table-viewer'
12
+ s.summary = %q{Easily view SNMP tables.}
13
+ s.description = %q{Easily view SNMP tables in a variety of different formats including as a table in the terminal, json or csv.}
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_runtime_dependency 'netsnmp', '~> 0.1.3'
21
+ s.add_runtime_dependency 'terminal-table', '~> 1.8.0'
22
+
23
+ s.add_development_dependency 'rake', '~> 12.0'
24
+ s.add_development_dependency 'rspec', '>= 2.14.1', '< 4'
25
+ s.add_development_dependency 'guard-rspec', '~> 4.2', '>= 4.2.5'
26
+ s.add_development_dependency 'rb-inotify', '~> 0.9'
27
+ s.add_development_dependency 'coveralls', '~> 0.7'
28
+ s.add_development_dependency 'simplecov', '~> 0.7'
29
+
30
+ end
@@ -0,0 +1,36 @@
1
+ describe SNMPTableViewer::Converter::IfTable do
2
+
3
+ describe '#convert' do
4
+
5
+ before :each do
6
+ @input = [[1, "lo", 24, 65536, nil, "", 1, 2, NETSNMP::Timetick.new(100), 18985, 220, 0, 0, 0, 0, 18985, 220, 0, 0, 0, nil, "0.0"]]
7
+ end
8
+
9
+ it 'Converts type' do
10
+ output = described_class.convert(@input)[0]
11
+ expect(output[2]).to eq '24 (softwareLoopback)'
12
+ end
13
+
14
+ it 'Converts admin state' do
15
+ output = described_class.convert(@input)[0]
16
+ expect(output[6]).to eq '1 (up)'
17
+ end
18
+
19
+ it 'Converts oper state' do
20
+ output = described_class.convert(@input)[0]
21
+ expect(output[7]).to eq '2 (down)'
22
+ end
23
+
24
+ it 'Converts last change' do
25
+ output = described_class.convert(@input)[0]
26
+ expect(output[8]).to eq 'Timeticks: (100) 0 days, 0:0:1.0'
27
+ end
28
+
29
+ end # describe #convert
30
+
31
+ end
32
+
33
+
34
+
35
+
36
+ # [1, "lo", 24, 65536, nil, "", 1, 1, NETSNMP::Timetick.new(100), 18985, 220, 0, 0, 0, 0, 18985, 220, 0, 0, 0, nil, "0.0"]
@@ -0,0 +1,2 @@
1
+ describe SNMPTableViewer::Converter do
2
+ end
@@ -0,0 +1,23 @@
1
+ describe SNMPTableViewer::Fetcher do
2
+
3
+ it 'Fetches' do
4
+ snmp_options = {opt_a: :a}
5
+ manager = double("manager")
6
+ expect(manager).to receive(:walk).with(oid: '1.2.3.4.5').once.and_return([
7
+ ['1.2.3.4.5.0.1.1', 1],
8
+ ['1.2.3.4.5.0.1.2', 2],
9
+ ['1.2.3.4.5.0.2.1', 'a'],
10
+ ['1.2.3.4.5.0.2.2', 'b'],
11
+ ['1.2.3.4.5.0.3.1', 'A'],
12
+ ['1.2.3.4.5.0.3.2', 'B'],
13
+ ]) # Array returned by manager doing the SNMP walk
14
+ expect(NETSNMP::Client).to receive(:new).with(snmp_options).once do |&block|
15
+ block.call(manager)
16
+ end # expect block
17
+ expect(described_class.from_snmp(base_oid: '1.2.3.4.5', **snmp_options)).to eq [
18
+ [1, 'a', 'A'],
19
+ [2, 'b', 'B']
20
+ ]
21
+ end
22
+
23
+ end
@@ -0,0 +1,26 @@
1
+ describe SNMPTableViewer::Formatter::CSV do
2
+
3
+ describe '#output' do
4
+
5
+ it 'With headings' do
6
+ headings = ['heading1', 'heading2', 'heading3']
7
+ data = [
8
+ ['row1col1', 'row1col2', 'a,b'],
9
+ ['row2col1', 'row2col2', 'b,c']
10
+ ]
11
+ formatter = described_class.new(data: data, headings: headings)
12
+ expect(formatter.output).to eq "heading1,heading2,heading3\nrow1col1,row1col2,\"a,b\"\nrow2col1,row2col2,\"b,c\"\n"
13
+ end
14
+
15
+ it 'Without headings' do
16
+ data = [
17
+ ['row1col1', 'row1col2', 'a,b'],
18
+ ['row2col1', 'row2col2', 'b,c']
19
+ ]
20
+ formatter = described_class.new(data: data)
21
+ expect(formatter.output).to eq "row1col1,row1col2,\"a,b\"\nrow2col1,row2col2,\"b,c\"\n"
22
+ end
23
+
24
+ end # describe #output
25
+
26
+ end
@@ -0,0 +1,26 @@
1
+ describe SNMPTableViewer::Formatter::JSON do
2
+
3
+ describe '#output' do
4
+
5
+ it 'With headings' do
6
+ headings = ['heading1', 'heading2', 'heading3']
7
+ data = [
8
+ ['row1col1', 'row1col2', 'a,b'],
9
+ ['row2col1', 'row2col2', 'b,c']
10
+ ]
11
+ formatter = described_class.new(data: data, headings: headings)
12
+ expect(formatter.output).to eq '[{"heading1":"row1col1","heading2":"row1col2","heading3":"a,b"},{"heading1":"row2col1","heading2":"row2col2","heading3":"b,c"}]'
13
+ end
14
+
15
+ it 'Without headings' do
16
+ data = [
17
+ ['row1col1', 'row1col2', 'a,b'],
18
+ ['row2col1', 'row2col2', 'b,c']
19
+ ]
20
+ formatter = described_class.new(data: data)
21
+ expect(formatter.output).to eq '[["row1col1","row1col2","a,b"],["row2col1","row2col2","b,c"]]'
22
+ end
23
+
24
+ end # describe #output
25
+
26
+ end
@@ -0,0 +1,26 @@
1
+ describe SNMPTableViewer::Formatter::Raw do
2
+
3
+ describe '#output' do
4
+
5
+ it 'With headings' do
6
+ headings = ['heading1', 'heading2', 'heading3']
7
+ data = [
8
+ ['row1col1', 'row1col2', 'a,b'],
9
+ ['row2col1', 'row2col2', 'b,c']
10
+ ]
11
+ formatter = described_class.new(data: data, headings: headings)
12
+ expect(formatter.output).to eq '[["heading1", "heading2", "heading3"], ["row1col1", "row1col2", "a,b"], ["row2col1", "row2col2", "b,c"]]'
13
+ end
14
+
15
+ it 'Without headings' do
16
+ data = [
17
+ ['row1col1', 'row1col2', 'a,b'],
18
+ ['row2col1', 'row2col2', 'b,c']
19
+ ]
20
+ formatter = described_class.new(data: data)
21
+ expect(formatter.output).to eq '[["row1col1", "row1col2", "a,b"], ["row2col1", "row2col2", "b,c"]]'
22
+ end
23
+
24
+ end # describe #output
25
+
26
+ end