rbeapi 1.1 → 1.2

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.
@@ -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')