net-ops 0.0.4.pre

Sign up to get free protection for your applications and to get access to all the features.
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: