net-ops 0.0.4.pre

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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/net/ops.rb +384 -0
  3. metadata +74 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f73921d344261f93ed38678f0be1ed30e058c786
4
+ data.tar.gz: 2dc6ca24c0ff3ef0fb849b96b9ed3f8967e1a9f8
5
+ SHA512:
6
+ metadata.gz: 6571aa3b754121c2218abfde7d7f08586022ac6b84944aa290a8faae0deb642a5e21ec757ba621c05aee747b807d66120f943abf07b8edcf1c8bc6678aa89ff3
7
+ data.tar.gz: c3f8fbab57e7c2642363b9f4ae492c66b3ed12cf87a5343b8a237e9d0f78e1bb4b97bec4e0c8f2b2dbd9cb44fd1d468fc200dac32d73ae3b54a474c6714c5009
data/lib/net/ops.rb ADDED
@@ -0,0 +1,384 @@
1
+ require 'yaml'
2
+ require 'logger'
3
+ require 'thread/pool'
4
+
5
+ Dir['../lib/net/transport/*.rb'].each { |file| require file }
6
+
7
+ module Net
8
+ module Ops
9
+
10
+ # Provides a DSL for interacting with Cisco switches and routers.
11
+ class Session
12
+
13
+ attr_reader :transport
14
+ attr_reader :transports
15
+
16
+ # Initialize a new session.
17
+ #
18
+ # @param [String] host the target host.
19
+ #
20
+ # @param [Hash<Symbol, String>] options an Hash containing the transport options.
21
+ # @option options [String] :timeout The timeout before raising an exception while waiting for output.
22
+ # @option options [String, Regexp] :prompt A String or a Regexp that match the prompt of the device.
23
+ #
24
+ # @param [Logger] logger the logger to use.
25
+ #
26
+ # @return [Session] the new session.
27
+ def initialize(host, options = { timeout: 10, prompt: /.+(#|>)/ }, logger = nil)
28
+ @host = host
29
+ @options = options
30
+ @transports = []
31
+
32
+ setup_logger(logger)
33
+
34
+ Net::Ops::Transport.constants.each do |c|
35
+ register_transport(c) if Class === Net::Ops::Transport.const_get(c)
36
+ end
37
+ end
38
+
39
+ # Open the session to the device.
40
+ #
41
+ # @param [Hash<Symbol, String>] credentials an Hash containing the credentials used to login.
42
+ # @option credentials [String] :username The username.
43
+ # @option credentials [String] :password The password.
44
+ #
45
+ # @return [void]
46
+ # @raise [Net::Ops::TransportUnavailable] if the session cannot be opened.
47
+ def open(credentials)
48
+ @credentials = credentials
49
+
50
+ @logger.debug(@host) { "Opening session as #{ credentials[:username] }" }
51
+
52
+ @transports.each do |transport|
53
+ @transport ||= transport.open(@host, @options, credentials)
54
+ end
55
+
56
+ fail Net::Ops::TransportUnavailable unless @transport
57
+ end
58
+
59
+ # Close the session to the device.
60
+ #
61
+ # @return [void]
62
+ def close
63
+ @logger.debug(@host) { 'Closing session' }
64
+ @transport.close
65
+ @transport = nil
66
+ end
67
+
68
+ # Measure the latency.
69
+ #
70
+ # @return [Integer] the time to run and retrieve the output of a command in milliseconds.
71
+ def latency
72
+ t1 = Time.now
73
+ @transport.cmd('')
74
+ t2 = Time.now
75
+
76
+ (t2 - t1) * 1000.0
77
+ end
78
+
79
+ # Send the specified command to the device and wait for the output.
80
+ #
81
+ # @param [String] command the command to run.
82
+ # @return [String] the output of the command.
83
+ def run(command)
84
+ @logger.debug("#{ @host } (#{ get_mode })") { "Executing #{ command }" }
85
+
86
+ output = ''
87
+ @transport.cmd(command) { |c| output += c }
88
+
89
+ @logger.debug("#{ @host } (#{ get_mode })") { output }
90
+ # @logger.warn(@host) { 'Net::Ops::IOSInvalidInput'; puts output } if /nvalid input detected/.match(output)
91
+ fail Net::Ops::IOSInvalidInput if /nvalid input detected/.match(output)
92
+
93
+ output
94
+ end
95
+
96
+ # Get the specified item on the device.
97
+ # Equivalent to the Cisco show command.
98
+ #
99
+ # @param item [String] the item to get.
100
+ # @return [String] the item.
101
+ def get(item)
102
+ run("show #{ item }")
103
+ end
104
+
105
+ # Set the value for the specified item on the device.
106
+ #
107
+ # @param item [String] the item to configure.
108
+ # @param value [String] the value to assign to the item.
109
+ # @return [String] the eventual output of the command.
110
+ def set(item, value)
111
+ run("#{ item } #{ value }")
112
+ end
113
+
114
+ # Enable the specified item on the device.
115
+ #
116
+ # @param item [String] the item to enable.
117
+ # @return [String] the eventual output of the command.
118
+ def enable(item)
119
+ run(item)
120
+ end
121
+
122
+ # Disable the specified item on the device.
123
+ # Equivalent to the Cisco no command.
124
+ #
125
+ # @param item [String] the item to enable.
126
+ # @return [String] the eventual output of the command.
127
+ def disable(item)
128
+ run("no #{ item }")
129
+ end
130
+
131
+ def zeroize(item)
132
+ @logger.debug(@host) { "Executing #{ item } zeroize" }
133
+
134
+ @transport.cmd('String' => "#{ item } zeroize", 'Match' => /.+/)
135
+ @transport.cmd('yes')
136
+ end
137
+
138
+ def generate(item, options)
139
+ run("#{ item } generate #{ options }")
140
+ end
141
+
142
+ # Run the specified command in the privileged mode on the device.
143
+ #
144
+ # @param [String] command the command to run.
145
+ # @return [String] the output of the command.
146
+ def exec(command)
147
+ ensure_mode(:privileged)
148
+ run(command)
149
+ end
150
+
151
+ # Run the specified command in the configuration mode on the device.
152
+ #
153
+ # @param [String] command the command to run.
154
+ # @return [String] the output of the command.
155
+ def config(command)
156
+ ensure_mode(:configuration)
157
+ run(command)
158
+ end
159
+
160
+ # Save the configuration of the device.
161
+ # Equivalent to the Cisco copy running-config startup-config command.
162
+ # @return [void]
163
+ def write!
164
+ ensure_mode(:privileged)
165
+ exec('write memory')
166
+ end
167
+
168
+ # Run the specified block in the privileged mode on the device.
169
+ #
170
+ # @param [Block] block the block to run.
171
+ # @return [void]
172
+ def privileged(&block)
173
+ ensure_mode(:privileged)
174
+ instance_eval(&block)
175
+ end
176
+
177
+ # Run the specified block in the configuration mode on the device.
178
+ #
179
+ # @param [Block] block the block to run.
180
+ # @return [void]
181
+ def configuration(options = nil, &block)
182
+ ensure_mode(:configuration)
183
+ instance_eval(&block)
184
+
185
+ write! if options == :enforce_save
186
+ end
187
+
188
+ def interface(interface, &block)
189
+ ensure_mode(:configuration)
190
+
191
+ run("interface #{ interface }")
192
+ instance_eval(&block)
193
+ end
194
+
195
+ def interfaces(interfaces = /.+/, &block)
196
+ ints = privileged do
197
+ get('interfaces status').select do |int|
198
+ interfaces.match("#{ int['short_type'] }#{ int['port_number'] }")
199
+ end
200
+ end
201
+
202
+ ints.each do |int|
203
+ interface("#{ int['short_type'] }#{ int['port_number'] }") do
204
+ instance_eval(&block)
205
+ end
206
+ end
207
+ end
208
+
209
+ def lines(lines, &block)
210
+ ensure_mode(:configuration)
211
+
212
+ run("line #{ lines }")
213
+ instance_eval(&block)
214
+ end
215
+
216
+ private
217
+
218
+ def register_transport(klass)
219
+ @logger.debug(@host) { "Registering transport #{ klass }" }
220
+ @transports << Net::Ops::Transport.const_get(klass)
221
+ end
222
+
223
+ # Create a default logger if none is specified.
224
+ #
225
+ # @param [Logger] logger the logger to use.
226
+ # @return [void]
227
+ def setup_logger(logger = nil)
228
+ # If a logger is specified we replace the existing.
229
+ @logger = logger
230
+
231
+ # Otherwise we create a new one.
232
+ # logger = Logger.new(STDOUT)
233
+ logger = Logger.new('log.log')
234
+ logger.level = Logger::DEBUG
235
+ @logger ||= logger
236
+ end
237
+
238
+ # Get the current command mode.
239
+ #
240
+ # @return [Symbol] the current command mode.
241
+ def get_mode
242
+ prompt = ''
243
+ @transport.cmd('') { |c| prompt += c }
244
+ match = /(?<hostname>[^\(-\)]+)(\((?<text>[\w\-]+)\))?(?<char>#|>)/.match(prompt)
245
+
246
+ mode = nil
247
+
248
+ if match && match['char']
249
+
250
+ mode = case match['char']
251
+ when '>' then :user
252
+ when '#' then :privileged
253
+ end
254
+
255
+ end
256
+
257
+ if match && match['text']
258
+ mode = match['text'].to_sym
259
+ end
260
+
261
+ mode
262
+ end
263
+
264
+ # Ensure the CLI is currently in the specified command mode.
265
+ #
266
+ # @param [Symbol] mode the target command mode.
267
+ # @return [void]
268
+ def ensure_mode(mode)
269
+ case mode
270
+
271
+ when :user
272
+ run('end') if configuration?
273
+
274
+ when :privileged
275
+ run('end') if configuration?
276
+ enable_privileged(@credentials['password']) if user?
277
+
278
+ when :configuration
279
+ run('configure terminal') unless configuration?
280
+
281
+ end
282
+ end
283
+
284
+ # Check if the CLI is in user mode.
285
+ #
286
+ # @return [Boolean]
287
+ def user?
288
+ get_mode == :user
289
+ end
290
+
291
+ # Check if the CLI is in privileged mode.
292
+ #
293
+ # @return [Boolean]
294
+ def privileged?
295
+ get_mode == :privileged
296
+ end
297
+
298
+ # Check if the CLI is in configuration mode.
299
+ #
300
+ # @return [Boolean]
301
+ def configuration?
302
+ get_mode.to_s.include?('config')
303
+ end
304
+
305
+ # Go from user mode to privileged mode.
306
+ #
307
+ # @param [String] the enable password.
308
+ # @return [void]
309
+ def enable_privileged(password)
310
+ @transport.cmd('String' => 'enable', 'Match' => /.+assword.+/)
311
+ @transport.cmd(password)
312
+ end
313
+
314
+ end
315
+
316
+ class Parser
317
+
318
+ def initialize(file)
319
+ @regexs = YAML.load_file(file)
320
+ end
321
+
322
+ def parse(command, output)
323
+ results = []
324
+ path = explore_tree(command.split(/ /))
325
+
326
+ if path.has_key?('regex')
327
+ regex = Regexp.new(path.fetch('regex').delete(' '))
328
+
329
+ output.each_line do |line|
330
+ results << regex.match(line) if regex.match(line)
331
+ end
332
+
333
+ else results = output
334
+ end
335
+
336
+ results
337
+ end
338
+
339
+ private
340
+
341
+ def explore_tree(path)
342
+ level = @regexs['cisco']
343
+
344
+ path.each { |p| level[p] ? level = level[p] : break }
345
+
346
+ level
347
+ end
348
+
349
+ end
350
+
351
+ class Task
352
+ include Net::Ops
353
+
354
+ def initialize(id)
355
+ @id = id
356
+
357
+ @logger = Logger.new(STDOUT)
358
+ @logger.level = Logger::INFO
359
+ end
360
+
361
+ def log(severity, message)
362
+ @logger.add(severity, message, @id)
363
+ end
364
+
365
+ def info(message)
366
+ log(Logger::INFO, message)
367
+ end
368
+
369
+ def warn(message)
370
+ log(Logger::WARN, message)
371
+ end
372
+
373
+ def error(message)
374
+ log(Logger::ERROR, message)
375
+ end
376
+
377
+ end
378
+
379
+ #
380
+ class TransportUnavailable < Exception; end
381
+ class IOSInvalidInput < Exception; end
382
+
383
+ end
384
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: net-ops
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4.pre
5
+ platform: ruby
6
+ authors:
7
+ - Maxime Mouchet
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thread
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '0.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '0.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: net-ssh-telnet
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.2
41
+ description: Framework for interacting with network devices.
42
+ email:
43
+ - max@maxmouchet.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - lib/net/ops.rb
49
+ homepage: http://github.com/maxmouchet/qscripts
50
+ licenses:
51
+ - MIT
52
+ metadata: {}
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - '>'
65
+ - !ruby/object:Gem::Version
66
+ version: 1.3.1
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 2.0.3
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: Net::Ops
73
+ test_files: []
74
+ has_rdoc: