net-ops 0.0.5.pre → 0.0.6.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,308 @@
1
+ module Net; module Ops
2
+
3
+ # Provides a DSL for interacting with Cisco switches and routers.
4
+ class Session
5
+
6
+ attr_reader :transport
7
+ attr_reader :transports
8
+
9
+ # Initialize a new session.
10
+ #
11
+ # @param [String] host the target host.
12
+ #
13
+ # @param [Hash<Symbol, String>] options an Hash containing the transport options.
14
+ # @option options [String] :timeout The timeout before raising an exception while waiting for output.
15
+ # @option options [String, Regexp] :prompt A String or a Regexp that match the prompt of the device.
16
+ #
17
+ # @param [Logger] logger the logger to use.
18
+ #
19
+ # @return [Session] the new session.
20
+ def initialize(host, options = { timeout: 10, prompt: /.+(#|>|\])/ }, logger = nil)
21
+ @host = host
22
+ @options = options
23
+ @transports = []
24
+
25
+ setup_logger(logger)
26
+
27
+ Net::Ops::Transport.constants.each do |c|
28
+ register_transport(c) if Class === Net::Ops::Transport.const_get(c)
29
+ end
30
+ end
31
+
32
+ # Open the session to the device.
33
+ #
34
+ # @param [Hash<Symbol, String>] credentials an Hash containing the credentials used to login.
35
+ # @option credentials [String] :username The username.
36
+ # @option credentials [String] :password The password.
37
+ #
38
+ # @return [void]
39
+ # @raise [Net::Ops::TransportUnavailable] if the session cannot be opened.
40
+ def open(credentials)
41
+ @credentials = credentials
42
+
43
+ @logger.debug(@host) { "Opening session as #{ credentials[:username] }" }
44
+
45
+ @transports.each do |transport|
46
+ @transport ||= transport.open(@host, @options, credentials)
47
+ end
48
+
49
+ fail Net::Ops::TransportUnavailable unless @transport
50
+ end
51
+
52
+ # Close the session to the device.
53
+ #
54
+ # @return [void]
55
+ def close
56
+ @logger.debug(@host) { 'Closing session' }
57
+ @transport.close
58
+ @transport = nil
59
+ end
60
+
61
+ # Measure the latency.
62
+ #
63
+ # @return [Integer] the time to run and retrieve the output of a command in milliseconds.
64
+ def latency
65
+ t1 = Time.now
66
+ @transport.cmd('')
67
+ t2 = Time.now
68
+
69
+ (t2 - t1) * 1000.0
70
+ end
71
+
72
+ # Send the specified command to the device and wait for the output.
73
+ #
74
+ # @param [String] command the command to run.
75
+ # @return [String] the output of the command.
76
+ def run(command)
77
+ @logger.debug("#{ @host } (#{ get_mode })") { "Executing #{ command }" }
78
+
79
+ output = ''
80
+ @transport.cmd(command) { |c| output += c }
81
+
82
+ # @logger.debug("#{ @host } (#{ get_mode })") { output }
83
+ # @logger.warn(@host) { 'Net::Ops::IOSInvalidInput'; puts output } if /nvalid input detected/.match(output)
84
+ fail Net::Ops::IOSInvalidInput if /nvalid input detected/.match(output)
85
+
86
+ output
87
+ end
88
+
89
+ # Get the specified item on the device.
90
+ # Equivalent to the Cisco show command.
91
+ #
92
+ # @param item [String] the item to get.
93
+ # @return [String] the item.
94
+ def get(item)
95
+ run("show #{ item }")
96
+ end
97
+
98
+ # Set the value for the specified item on the device.
99
+ #
100
+ # @param item [String] the item to configure.
101
+ # @param value [String] the value to assign to the item.
102
+ # @return [String] the eventual output of the command.
103
+ def set(item, value)
104
+ run("#{ item } #{ value }")
105
+ end
106
+
107
+ # Enable the specified item on the device.
108
+ #
109
+ # @param item [String] the item to enable.
110
+ # @return [String] the eventual output of the command.
111
+ def enable(item)
112
+ run(item)
113
+ end
114
+
115
+ # Disable the specified item on the device.
116
+ # Equivalent to the Cisco no command.
117
+ #
118
+ # @param item [String] the item to enable.
119
+ # @return [String] the eventual output of the command.
120
+ def disable(item)
121
+ run("no #{ item }")
122
+ end
123
+
124
+ def zeroize(item)
125
+ @logger.debug(@host) { "Executing #{ item } zeroize" }
126
+
127
+ @transport.cmd('String' => "#{ item } zeroize", 'Match' => /.+/)
128
+ @transport.cmd('yes')
129
+ end
130
+
131
+ def generate(item, options)
132
+ run("#{ item } generate #{ options }")
133
+ end
134
+
135
+ # Run the specified command in the privileged mode on the device.
136
+ #
137
+ # @param [String] command the command to run.
138
+ # @return [String] the output of the command.
139
+ def exec(command)
140
+ ensure_mode(:privileged)
141
+ run(command)
142
+ end
143
+
144
+ # Run the specified command in the configuration mode on the device.
145
+ #
146
+ # @param [String] command the command to run.
147
+ # @return [String] the output of the command.
148
+ def config(command)
149
+ ensure_mode(:configuration)
150
+ run(command)
151
+ end
152
+
153
+ # Save the configuration of the device.
154
+ # Equivalent to the Cisco copy running-config startup-config command.
155
+ # @return [void]
156
+ def write!
157
+ ensure_mode(:privileged)
158
+ exec('write memory')
159
+ end
160
+
161
+ # Run the specified block in the privileged mode on the device.
162
+ #
163
+ # @param [Block] block the block to run.
164
+ # @return [void]
165
+ def privileged(&block)
166
+ ensure_mode(:privileged)
167
+ instance_eval(&block)
168
+ end
169
+
170
+ # Run the specified block in the configuration mode on the device.
171
+ #
172
+ # @param [Block] block the block to run.
173
+ # @return [void]
174
+ def configuration(options = nil, &block)
175
+ ensure_mode(:configuration)
176
+ instance_eval(&block)
177
+
178
+ write! if options == :enforce_save
179
+ end
180
+
181
+ def interface(interface, &block)
182
+ ensure_mode(:configuration)
183
+
184
+ run("interface #{ interface }")
185
+ instance_eval(&block)
186
+ end
187
+
188
+ def interfaces(interfaces = /.+/, &block)
189
+ ints = privileged do
190
+ get('interfaces status').select do |int|
191
+ interfaces.match("#{ int['short_type'] }#{ int['port_number'] }")
192
+ end
193
+ end
194
+
195
+ ints.each do |int|
196
+ interface("#{ int['short_type'] }#{ int['port_number'] }") do
197
+ instance_eval(&block)
198
+ end
199
+ end
200
+ end
201
+
202
+ def lines(lines, &block)
203
+ ensure_mode(:configuration)
204
+
205
+ run("line #{ lines }")
206
+ instance_eval(&block)
207
+ end
208
+
209
+ private
210
+
211
+ def register_transport(klass)
212
+ @logger.debug(@host) { "Registering transport #{ klass }" }
213
+ @transports << Net::Ops::Transport.const_get(klass)
214
+ end
215
+
216
+ # Create a default logger if none is specified.
217
+ #
218
+ # @param [Logger] logger the logger to use.
219
+ # @return [void]
220
+ def setup_logger(logger = nil)
221
+ # If a logger is specified we replace the existing.
222
+ @logger = logger
223
+
224
+ # Otherwise we create a new one.
225
+ logger = Logger.new(STDOUT)
226
+ logger.level = Logger::DEBUG
227
+ @logger ||= logger
228
+ end
229
+
230
+ # Get the current command mode.
231
+ #
232
+ # @return [Symbol] the current command mode.
233
+ def get_mode
234
+ prompt = ''
235
+ @transport.cmd('') { |c| prompt += c }
236
+ match = /(?<hostname>[^\(-\)]+)(\((?<text>[\w\-]+)\))?(?<char>#|>)/.match(prompt)
237
+
238
+ mode = nil
239
+
240
+ if match && match['char']
241
+
242
+ mode = case match['char']
243
+ when '>' then :user
244
+ when '#' then :privileged
245
+ end
246
+
247
+ end
248
+
249
+ if match && match['text']
250
+ mode = match['text'].to_sym
251
+ end
252
+
253
+ mode
254
+ end
255
+
256
+ # Ensure the CLI is currently in the specified command mode.
257
+ #
258
+ # @param [Symbol] mode the target command mode.
259
+ # @return [void]
260
+ def ensure_mode(mode)
261
+ case mode
262
+
263
+ when :user
264
+ run('end') if configuration?
265
+
266
+ when :privileged
267
+ run('end') if configuration?
268
+ enable_privileged(@credentials[:password]) if user?
269
+
270
+ when :configuration
271
+ run('configure terminal') unless configuration?
272
+
273
+ end
274
+ end
275
+
276
+ # Check if the CLI is in user mode.
277
+ #
278
+ # @return [Boolean]
279
+ def user?
280
+ get_mode == :user
281
+ end
282
+
283
+ # Check if the CLI is in privileged mode.
284
+ #
285
+ # @return [Boolean]
286
+ def privileged?
287
+ get_mode == :privileged
288
+ end
289
+
290
+ # Check if the CLI is in configuration mode.
291
+ #
292
+ # @return [Boolean]
293
+ def configuration?
294
+ get_mode.to_s.include?('config')
295
+ end
296
+
297
+ # Go from user mode to privileged mode.
298
+ #
299
+ # @param [String] the enable password.
300
+ # @return [void]
301
+ def enable_privileged(password)
302
+ @transport.cmd('String' => 'enable', 'Match' => /.+assword.+/)
303
+ @transport.cmd(password)
304
+ end
305
+
306
+ end
307
+
308
+ end; end
@@ -0,0 +1,32 @@
1
+ module Net; module Ops
2
+
3
+ #
4
+ class Task
5
+ include Net::Ops
6
+
7
+ def initialize(id)
8
+ @id = id
9
+
10
+ @logger = Logger.new(STDOUT)
11
+ @logger.level = Logger::INFO
12
+ end
13
+
14
+ def log(severity, message)
15
+ @logger.add(severity, message, @id)
16
+ end
17
+
18
+ def info(message)
19
+ log(Logger::INFO, message)
20
+ end
21
+
22
+ def warn(message)
23
+ log(Logger::WARN, message)
24
+ end
25
+
26
+ def error(message)
27
+ log(Logger::ERROR, message)
28
+ end
29
+
30
+ end
31
+
32
+ end; end
@@ -0,0 +1,35 @@
1
+ require 'net/ssh/telnet'
2
+
3
+ module Net; module Ops; module Transport
4
+
5
+ #
6
+ class SSH
7
+
8
+ # Open an SSH session to the specified host using net/ssh/telnet.
9
+ #
10
+ # @param host [String] the destination host.
11
+ # @param options [Hash]
12
+ # @param credentials [Hash] credentials to use to connect.
13
+ def self.open(host, options, credentials)
14
+ session = nil
15
+
16
+ ssh = Net::SSH.start(host, credentials[:username], :password => credentials[:password])
17
+ session = Net::SSH::Telnet.new('Session' => ssh,
18
+ 'Timeout' => options[:timeout],
19
+ 'Prompt' => options[:prompt])
20
+
21
+ rescue Errno::ECONNREFUSED => e
22
+ session = nil
23
+
24
+ rescue Net::SSH::AuthenticationFailed => e
25
+ session = nil
26
+
27
+ rescue Exception => e
28
+ session = nil
29
+
30
+ return session
31
+ end
32
+
33
+ end
34
+
35
+ end; end; end
@@ -0,0 +1,49 @@
1
+ require 'net/telnet'
2
+
3
+ module Net; module Ops; module Transport
4
+
5
+ #
6
+ class Telnet
7
+
8
+ # Open a Telnet session to the specified host using net/ssh.
9
+ #
10
+ # @param host [String] the destination host.
11
+ # @param options [Hash]
12
+ # @param credentials [Hash] credentials to use to connect.
13
+ def self.open(host, options, credentials)
14
+ session = nil
15
+
16
+ session = Net::Telsnet.new('Host' => host,
17
+ 'Timeout' => options[:timeout],
18
+ 'Prompt' => options[:prompt])
19
+
20
+ output = ''
21
+ session.cmd('String' => '', 'Match' => /.+/) { |c| output += c }
22
+
23
+ if /[Uu]sername:/.match(output) then
24
+ session.cmd('String' => credentials[:username],
25
+ 'Match' => /.+/)
26
+ session.cmd(credentials[:password])
27
+ end
28
+
29
+ if /[Pp]assword:/.match(output) then
30
+ session.cmd(credentials[:password])
31
+ end
32
+
33
+ return session
34
+
35
+ rescue Errno::ECONNREFUSED => e
36
+ session = nil
37
+
38
+ rescue Net::OpenTimeout => e
39
+ session = nil
40
+
41
+ rescue Exception => e
42
+ session = nil
43
+
44
+ return session
45
+ end
46
+
47
+ end
48
+
49
+ end; end; end
@@ -0,0 +1,5 @@
1
+ module Net
2
+ module Ops
3
+ VERSION = '0.0.6.pre'
4
+ end
5
+ end
data/net-ops.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'net/ops/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'net-ops'
8
+ spec.version = Net::Ops::VERSION
9
+ spec.authors = ['Maxime Mouchet']
10
+ spec.email = ['mouchet.max@gmail.com']
11
+ spec.description = %q{Framework to automate daily operations on network devices.}
12
+ spec.summary = %q{Net::Ops}
13
+ spec.homepage = 'http://github.com/maxmouchet/qscripts'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency 'thread', '~> 0.1'
22
+ spec.add_runtime_dependency 'net-ssh-telnet', '~> 0.0.2'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.3'
25
+ spec.add_development_dependency 'rake'
26
+ end