rbeapi 1.1 → 1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -41,13 +41,35 @@ module Rbeapi
41
41
  # The Ntp class provides an instance for working with the nodes
42
42
  # NTP configuration.
43
43
  class Ntp < Entity
44
+ DEFAULT_TRST_KEY = ''.freeze
44
45
  DEFAULT_SRC_INTF = ''.freeze
45
46
 
47
+ # Regular expression to extract a NTP server's attributes from the
48
+ # running-configuration text. The explicit [ ] spaces enable line
49
+ # wrapping and indentation with the /x flag.
50
+
51
+ SERVER_REGEXP = /^(?:ntp[ ]server)
52
+ (?:(?:[ ]vrf[ ])([^\s]+))?
53
+ [ ]([^\s]+)
54
+ ([ ]prefer)?
55
+ (?:(?:[ ]minpoll[ ])(\d+))?
56
+ (?:(?:[ ]maxpoll[ ])(\d+))?
57
+ (?:(?:[ ]source[ ])([^\s]+))?
58
+ (?:(?:[ ]key[ ])(\d+))?/x
59
+ ##
60
+
61
+ # Regular expression to extract NTP authentication-keys from the
62
+ # running-configuration text. The explicit [ ] spaces enable line
63
+ # wrapping and indentation with the /x flag.
64
+
65
+ AUTH_KEY_REGEXP = /^(?:ntp[ ]authentication-key[ ])
66
+ (\d+)[ ](\w+)[ ](\d+)[ ](\w+)/x
46
67
  ##
47
68
  # get returns the nodes current ntp configure as a resource hash.
48
69
  #
49
70
  # @example
50
71
  # {
72
+ # authenticate: [true, false],
51
73
  # source_interface: <string>,
52
74
  # servers: {
53
75
  # prefer: [true, false]
@@ -58,11 +80,52 @@ module Rbeapi
58
80
  # Hash.
59
81
  def get
60
82
  response = {}
83
+ response.merge!(parse_authenticate)
61
84
  response.merge!(parse_source_interface)
62
85
  response.merge!(parse_servers)
86
+ response.merge!(parse_trusted_key)
87
+ response.merge!(parse_auth_keys)
63
88
  response
64
89
  end
65
90
 
91
+ ##
92
+ # parse_authenticate checks to see if NTP authencation is enabled in conf
93
+ #
94
+ # @api private
95
+ #
96
+ # @return [Hash<Symbol, Object>] Returns the resource hash attribute.
97
+ def parse_authenticate
98
+ mdata = /^(?:ntp authenticate)/.match(config)
99
+ { authenticate: mdata.nil? ? false : true }
100
+ end
101
+ private :parse_authenticate
102
+
103
+ ##
104
+ # parse_auth_keys scans the nodes configuration and parses the configured
105
+ # authencation keys. This method will also return
106
+ # the value of prefer. If no keys are configured, the value will be
107
+ # set to an empty array. The return hash is intended to be merged into
108
+ # the resource hash.
109
+ #
110
+ # @api private
111
+ #
112
+ # @return [Hash<Symbol, Object>] Returns the resource hash attribute.
113
+ def parse_auth_keys
114
+ tuples = config.scan(AUTH_KEY_REGEXP)
115
+ hsh = {}
116
+ tuples.map do |(key, algorithm, mode, password)|
117
+ hsh[key] = {
118
+ algorithm: algorithm,
119
+ mode: mode,
120
+ password: password
121
+ }
122
+ hsh[key]
123
+ end
124
+
125
+ { auth_keys: hsh }
126
+ end
127
+ private :parse_auth_keys
128
+
66
129
  ##
67
130
  # parse_source_interface scans the nodes configurations and parses
68
131
  # the ntp source interface if configured. If the source interface
@@ -89,14 +152,123 @@ module Rbeapi
89
152
  #
90
153
  # @return [Hash<Symbol, Object>] Returns the resource hash attribute.
91
154
  def parse_servers
92
- servers = config.scan(/(?:ntp server\s)([^\s]+)\s(prefer)?/)
93
- values = servers.each_with_object({}) do |(srv, prefer), hsh|
94
- hsh[srv] = { prefer: !prefer.nil? }
155
+ tuples = config.scan(SERVER_REGEXP)
156
+ hsh = {}
157
+ tuples.map do |(vrf, host, prefer, minpoll, maxpoll, sourcei, key)|
158
+ hsh[host] = {
159
+ vrf: vrf,
160
+ prefer: !prefer.nil?,
161
+ minpoll: minpoll.nil? ? nil : minpoll.to_i,
162
+ maxpoll: maxpoll.nil? ? nil : maxpoll.to_i,
163
+ source_interface: sourcei,
164
+ key: key.nil? ? nil : key.to_i
165
+ }
166
+ hsh[host]
95
167
  end
96
- { servers: values }
168
+
169
+ { servers: hsh }
97
170
  end
98
171
  private :parse_servers
99
172
 
173
+ ##
174
+ # parse_trusted_key looks for global NTP trusted-key list
175
+ #
176
+ # @api private
177
+ #
178
+ # @return [Hash<Symbol, Object>] Returns the resource hash attribute.
179
+ def parse_trusted_key
180
+ mdata = /^(?:ntp trusted-key (.+))/.match(config)
181
+ { trusted_key: mdata.nil? ? DEFAULT_TRST_KEY : mdata[1] }
182
+ end
183
+ private :parse_trusted_key
184
+
185
+ ##
186
+ # set_authenticate configures ntp authentication in the nodes
187
+ # running configuration. If the enable keyword is false, then
188
+ # ntp authentication is configured with the no keyword argument. If the
189
+ # default keyword argument is provided and set to true, the value is
190
+ # configured used the default keyword. The default keyword takes
191
+ # precedence over the enable keyword if both options are specified.
192
+ #
193
+ # @since eos_version 4.13.7M
194
+ #
195
+ # ===Commands
196
+ # ntp authenticate
197
+ # no ntp authenticate
198
+ # default ntp authenticate
199
+ #
200
+ # @param opts [Hash] Optional keyword arguments.
201
+ #
202
+ # @option opts enable [Boolean] If false then the command is
203
+ # negated. Default is true.
204
+ #
205
+ # @option opts default [Boolean] Configure the ntp source value using
206
+ # the default keyword.
207
+ #
208
+ # @return [Boolean] Returns true if the command completed successfully.
209
+ def set_authenticate(opts = {})
210
+ cmd = command_builder('ntp authenticate', opts)
211
+ configure(cmd)
212
+ end
213
+
214
+ ##
215
+ # set_authentication_key configures the ntp authentication-keys in the
216
+ # device running configuration. If the enable keyword is false, then
217
+ # the ntp source is configured with the no keyword argument. If the
218
+ # default keyword argument is provided and set to true, the value is
219
+ # configured used the default keyword. The default keyword takes
220
+ # precedence over the enable keyword if both options are specified.
221
+ #
222
+ # @since eos_version 4.13.7M
223
+ #
224
+ # ===Commands
225
+ # ntp trusted-key <key> <algorithm> <mode> <password>
226
+ # no ntp trusted-key <key>
227
+ # default ntp trusted-key <key>
228
+ #
229
+ # @param opts [Hash] Optional keyword arguments.
230
+ #
231
+ # @option opts algorithm [String] Encryption algorithm to use, md5/sha1
232
+ #
233
+ # @option opts default [Boolean] Configure the ntp source value using
234
+ # the default keyword.
235
+ #
236
+ # @option opts enable [Boolean] If false then the command is
237
+ # negated. Default is true.
238
+ #
239
+ # @option opts key [Integer] The authentication-key to configure
240
+ #
241
+ # @option opts mode [Integer] Password mode: 0 plain-text, 7 encrypted
242
+ # default value is 7
243
+ #
244
+ # @option opts password [String] Password to use for authentication-key
245
+ #
246
+ # @return [Boolean] Returns true if the command completed successfully.
247
+ def set_authentication_key(opts = {})
248
+ cmd = command_builder('ntp authentication-key', opts)
249
+ configure(cmd)
250
+
251
+ algorithm = opts[:algorithm]
252
+ default = opts[:default] || false
253
+ enable = opts.fetch(:enable, true)
254
+ key = opts[:key]
255
+ mode = opts.fetch(:mode, 7)
256
+ password = opts[:password]
257
+
258
+ case default
259
+ when true
260
+ cmds = "default ntp authentication-key #{key}"
261
+ when false
262
+ cmds = if enable
263
+ "ntp authentication-key #{key} #{algorithm} #{mode} "\
264
+ "#{password}"
265
+ else
266
+ "no ntp authentication-key #{key}"
267
+ end
268
+ end
269
+ configure cmds
270
+ end
271
+
100
272
  ##
101
273
  # set_source_interface configures the ntp source value in the nodes
102
274
  # running configuration. If the enable keyword is false, then
@@ -129,6 +301,37 @@ module Rbeapi
129
301
  configure(cmd)
130
302
  end
131
303
 
304
+ ##
305
+ # set_trusted_key configures the ntp trusted-keys in the device
306
+ # running configuration. If the enable keyword is false, then
307
+ # the ntp authentication-key is configured with the no keyword argument.
308
+ # If the default keyword argument is provided and set to true, the value
309
+ # is configured using the default keyword. The default keyword takes
310
+ # precedence over the enable keyword if both options are specified.
311
+ #
312
+ # @since eos_version 4.13.7M
313
+ #
314
+ # ===Commands
315
+ # ntp authentication-key <key> <algorithm> <mode> <password>
316
+ # no ntp authentication-key <key>
317
+ # default ntp trusted-key <key>
318
+ #
319
+ # @param opts [Hash] Optional keyword arguments.
320
+ #
321
+ # @option opts value [Integer] authentication-key id
322
+ #
323
+ # @option opts enable [Boolean] If false then the command is
324
+ # negated. Default is true.
325
+ #
326
+ # @option opts default [Boolean] Configure the ntp source value using
327
+ # the default keyword.
328
+ #
329
+ # @return [Boolean] Returns true if the command completed successfully.
330
+ def set_trusted_key(opts = {})
331
+ cmd = command_builder('ntp trusted-key', opts)
332
+ configure(cmd)
333
+ end
334
+
132
335
  ##
133
336
  # add_server configures a new ntp server destination hostname or ip
134
337
  # address to the list of ntp destinations. The optional prefer argument
@@ -138,14 +341,33 @@ module Rbeapi
138
341
  # @param server [String] The IP address or FQDN of the NTP server to
139
342
  # be removed from the configuration.
140
343
  #
344
+ # @param opts [hash] Optional keyword arguments.
345
+ #
346
+ # @param opts vrf [String] The VRF instance this server is bound to
347
+ #
348
+ # @param opts minpoll [Integer] The minimum poll interval
349
+ #
350
+ # @param opts maxpoll [Integer] The maximum poll interval
351
+ #
352
+ # @param opts source [String] The source interface used to reach server
353
+ #
354
+ # @param opts key [Integer] The authentication key used to communicate
355
+ # with server
356
+ #
141
357
  # @param prefer [Boolean] Appends the prefer keyword argument to the
142
358
  # command if this value is true.
143
359
  #
144
360
  # @return [Boolean] Returns true if the command completed successfully.
145
- def add_server(server, prefer = false)
146
- cmd = "ntp server #{server}"
147
- cmd << ' prefer' if prefer
148
- configure cmd
361
+ def add_server(server, prefer = false, opts = {})
362
+ cmd = 'ntp server '
363
+ cmd << "vrf #{opts[:vrf]} " if opts[:vrf]
364
+ cmd << server.to_s
365
+ cmd << ' prefer' if prefer || opts[:prefer].to_s == 'true'
366
+ cmd << " minpoll #{opts[:minpoll]} " if opts[:minpoll]
367
+ cmd << " maxpoll #{opts[:maxpoll]} " if opts[:maxpoll]
368
+ cmd << " source #{opts[:source_interface]} " if opts[:source_interface]
369
+ cmd << " key #{opts[:key]} " if opts[:key]
370
+ configure(cmd)
149
371
  end
150
372
 
151
373
  ##
@@ -156,9 +378,12 @@ module Rbeapi
156
378
  # @param server [String] The IP address or FQDN of the NTP server to
157
379
  # be removed from the configuration.
158
380
  #
381
+ # @param vrf [String] The VRF of the NTP server to be removed from
382
+ # the configuration.
383
+ #
159
384
  # @return [Boolean] Returns true if the command completed successfully.
160
- def remove_server(server)
161
- configure("no ntp server #{server}")
385
+ def remove_server(server, vrf = nil)
386
+ configure("no ntp server #{vrf.nil? ? '' : "vrf #{vrf} "}#{server}")
162
387
  end
163
388
 
164
389
  ##
@@ -137,7 +137,7 @@ module Rbeapi
137
137
  #
138
138
  # @return [Hash<Symbol, Object>] Returns the resource Hash attribute.
139
139
  def parse_source_interface
140
- mdata = /snmp-server source-interface (.+)$/.match(config)
140
+ mdata = /snmp-server (?:vrf .+ )?source-interface (.+)$/.match(config)
141
141
  { source_interface: mdata.nil? ? '' : mdata[1] }
142
142
  end
143
143
  private :parse_source_interface
@@ -63,6 +63,7 @@ module Rbeapi
63
63
  response = {}
64
64
  response.merge!(parse_hostname(config))
65
65
  response.merge!(parse_iprouting(config))
66
+ response.merge!(parse_timezone(config))
66
67
  response.merge!(parse_banners(config))
67
68
  response
68
69
  end
@@ -97,6 +98,21 @@ module Rbeapi
97
98
  end
98
99
  private :parse_iprouting
99
100
 
101
+ ##
102
+ # parse_timezone parses the value of clock timezone.
103
+ #
104
+ # @api private
105
+ #
106
+ # @param config [String] The configuration block returned
107
+ # from the node's running configuration.
108
+ #
109
+ # @return [Hash<Symbol, Object>] The resource hash attribute.
110
+ def parse_timezone(config)
111
+ mdata = /(?<=^clock timezone\s)(.+)$/.match(config)
112
+ { timezone: mdata.nil? ? '' : mdata[1] }
113
+ end
114
+ private :parse_timezone
115
+
100
116
  ##
101
117
  # Parses the global config and returns the value for both motd
102
118
  # and login banners.
@@ -158,6 +174,25 @@ module Rbeapi
158
174
  configure(cmd)
159
175
  end
160
176
 
177
+ ##
178
+ # Configures the value of clock timezone in the running-config.
179
+ #
180
+ # @param opts [Hash] The configuration parameters.
181
+ #
182
+ # @option opts value [string] The value to set the clock timezone to.
183
+ #
184
+ # @option opts enable [Boolean] If false then the command is
185
+ # negated. Default is true.
186
+ #
187
+ # @option opts default [Boolean] If true configure the command using
188
+ # the default keyword. Default is false.
189
+ #
190
+ # @return [Boolean] Returns true if the command completed successfully.
191
+ def set_timezone(opts = {})
192
+ cmd = command_builder('clock timezone', opts)
193
+ configure(cmd)
194
+ end
195
+
161
196
  ##
162
197
  # Configures system banners.
163
198
  #
@@ -33,5 +33,5 @@
33
33
  # #
34
34
  # Rbeapi toplevel namespace.
35
35
  module Rbeapi
36
- VERSION = '1.1'.freeze
36
+ VERSION = '1.2'.freeze
37
37
  end
@@ -1,5 +1,8 @@
1
+ #[connection:puppet-dev-dut]
1
2
  [connection:dut]
2
- host: 192.168.1.16
3
- username: eapi
4
- password: password
3
+ host: localhost
5
4
  transport: http
5
+ port: 61080
6
+ #port: 61443
7
+ username: eapiuser
8
+ password: icanttellyou
@@ -74,7 +74,7 @@ module FixtureHelpers
74
74
  json = Pathname.new(File.join(dir, "fixture_#{key}.json"))
75
75
  text = Pathname.new(File.join(dir, "fixture_#{key}.text"))
76
76
 
77
- data = if yaml.exist?; then YAML.load(File.read(yaml))
77
+ data = if yaml.exist?; then YAML.safe_load(File.read(yaml))
78
78
  elsif json.exist?; then JSON.parse(File.read(json))
79
79
  elsif text.exist?; then File.read(text)
80
80
  else raise "could not load YAML, JSON or TEXT fixture #{key} "\
@@ -0,0 +1,142 @@
1
+ #
2
+ # Copyright (c) 2017, Arista Networks, Inc.
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are
7
+ # met:
8
+ #
9
+ # Redistributions of source code must retain the above copyright notice,
10
+ # this list of conditions and the following disclaimer.
11
+ #
12
+ # Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ #
16
+ # Neither the name of Arista Networks nor the names of its
17
+ # contributors may be used to endorse or promote products derived from
18
+ # this software without specific prior written permission.
19
+ #
20
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARISTA NETWORKS
24
+ # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
27
+ # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28
+ # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
29
+ # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
30
+ # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+ #
32
+ require 'spec_helper'
33
+
34
+ require 'rbeapi/client'
35
+ require 'rbeapi/api/iphosts'
36
+
37
+ describe Rbeapi::Api::Iphosts do
38
+ subject { described_class.new(node) }
39
+
40
+ let(:node) do
41
+ Rbeapi::Client.config.read(fixture_file('dut.conf'))
42
+ Rbeapi::Client.connect_to('dut')
43
+ end
44
+
45
+ let(:ipaddress) do
46
+ '192.168.0.1'
47
+ end
48
+
49
+ let(:test) do
50
+ {
51
+ name: 'test1',
52
+ ipaddress: ['192.168.0.1']
53
+ }
54
+ end
55
+
56
+ describe '#getall' do
57
+ let(:resource) { subject.getall }
58
+
59
+ let(:test1_entries) do
60
+ {
61
+ 'test1' => { name: 'test1', ipaddress: ['192.168.0.1'] },
62
+ 'test2' => { name: 'test2', ipaddress: ['10.0.0.1', '10.0.1.1'] },
63
+ 'test3.domain' => { name: 'test3.domain', ipaddress: ['172.16.0.1'] }
64
+ }
65
+ end
66
+
67
+ before do
68
+ node.config(['no ip host test1',
69
+ 'no ip host test2',
70
+ 'no ip host test3.domain',
71
+ 'ip host test1 192.168.0.1',
72
+ 'ip host test2 10.0.0.1 10.0.1.1',
73
+ 'ip host test3.domain 172.16.0.1'])
74
+ end
75
+
76
+ it 'returns the ip host collection' do
77
+ expect(subject.getall).to include(test1_entries)
78
+ end
79
+
80
+ it 'returns a hash collection' do
81
+ expect(subject.getall).to be_a_kind_of(Hash)
82
+ end
83
+
84
+ it 'returns the ip host resource for given host' do
85
+ expect(subject.get('test1')).to eq(test)
86
+ end
87
+
88
+ it 'returns a hash' do
89
+ expect(subject.get('test1')).to be_a_kind_of(Hash)
90
+ end
91
+
92
+ it 'has 2 entries' do
93
+ expect(subject.get('test1').size).to eq(2)
94
+ end
95
+ end
96
+
97
+ describe '#create' do
98
+ before do
99
+ node.config(['no ip host test1'])
100
+ end
101
+
102
+ it 'create a new ip host name' do
103
+ expect(subject.get('test1')).to eq(nil)
104
+ expect(subject.create('test1', ipaddress: ['192.168.0.1'])).to be_truthy
105
+ expect(subject.get('test1')[:ipaddress]).to eq(['192.168.0.1'])
106
+ end
107
+
108
+ it 'raises ArgumentError for create without required args ' do
109
+ expect { subject.create('test1') }.to \
110
+ raise_error ArgumentError
111
+ end
112
+
113
+ it 'raises ArgumentError for invalid ipaddress value' do
114
+ expect { subject.create('test1', ipaddress: ['bogus']) }.to \
115
+ raise_error ArgumentError
116
+ end
117
+ end
118
+
119
+ describe '#delete' do
120
+ before do
121
+ node.config(['ip host test12 172.16.0.12'])
122
+ end
123
+
124
+ it 'delete an ip host resource' do
125
+ expect(subject.get('test12')[:name]).to eq('test12')
126
+ expect(subject.delete('test12')).to be_truthy
127
+ expect(subject.get('test12')).to eq(nil)
128
+ end
129
+ end
130
+
131
+ describe '#set_ipaddress' do
132
+ before do
133
+ node.config(['no ip host test13'])
134
+ end
135
+
136
+ it 'change the ip address' do
137
+ expect(subject.create('test13', ipaddress: ['192.168.0.1'])).to be_truthy
138
+ expect(subject.create('test13', ipaddress: ['172.16.0.13'])).to be_truthy
139
+ expect(subject.get('test13')[:ipaddress]).to eq(['172.16.0.13'])
140
+ end
141
+ end
142
+ end
@@ -23,6 +23,7 @@ describe Rbeapi::Api::Managementdefaults do
23
23
  before { node.config(['management defaults', 'default secret hash']) }
24
24
 
25
25
  it 'configures the management defaults secret hash value' do
26
+ expect(subject.set_secret_hash(value: 'md5')).to be_truthy
26
27
  expect(subject.get[:secret_hash]).to eq('md5')
27
28
  expect(subject.set_secret_hash(value: 'sha512')).to be_truthy
28
29
  expect(subject.get[:secret_hash]).to eq('sha512')