rbeapi 0.1.0

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 (60) hide show
  1. data/.gitignore +35 -0
  2. data/Gemfile +25 -0
  3. data/Guardfile +15 -0
  4. data/LICENSE +28 -0
  5. data/README.md +218 -0
  6. data/Rakefile +12 -0
  7. data/lib/rbeapi.rb +32 -0
  8. data/lib/rbeapi/api.rb +135 -0
  9. data/lib/rbeapi/api/aaa.rb +410 -0
  10. data/lib/rbeapi/api/dns.rb +198 -0
  11. data/lib/rbeapi/api/interfaces.rb +1193 -0
  12. data/lib/rbeapi/api/ipinterfaces.rb +328 -0
  13. data/lib/rbeapi/api/logging.rb +157 -0
  14. data/lib/rbeapi/api/mlag.rb +519 -0
  15. data/lib/rbeapi/api/ntp.rb +201 -0
  16. data/lib/rbeapi/api/ospf.rb +214 -0
  17. data/lib/rbeapi/api/prefixlists.rb +98 -0
  18. data/lib/rbeapi/api/radius.rb +317 -0
  19. data/lib/rbeapi/api/radius.rb.old +399 -0
  20. data/lib/rbeapi/api/routemaps.rb +100 -0
  21. data/lib/rbeapi/api/snmp.rb +427 -0
  22. data/lib/rbeapi/api/staticroutes.rb +88 -0
  23. data/lib/rbeapi/api/stp.rb +381 -0
  24. data/lib/rbeapi/api/switchports.rb +272 -0
  25. data/lib/rbeapi/api/system.rb +87 -0
  26. data/lib/rbeapi/api/tacacs.rb +236 -0
  27. data/lib/rbeapi/api/varp.rb +181 -0
  28. data/lib/rbeapi/api/vlans.rb +338 -0
  29. data/lib/rbeapi/client.rb +454 -0
  30. data/lib/rbeapi/eapilib.rb +334 -0
  31. data/lib/rbeapi/netdev/snmp.rb +370 -0
  32. data/lib/rbeapi/utils.rb +70 -0
  33. data/lib/rbeapi/version.rb +37 -0
  34. data/rbeapi.gemspec +32 -0
  35. data/spec/fixtures/dut.conf +5 -0
  36. data/spec/spec_helper.rb +22 -0
  37. data/spec/support/fixtures.rb +114 -0
  38. data/spec/support/shared_examples_for_api_modules.rb +124 -0
  39. data/spec/system/api_ospf_interfaces_spec.rb +58 -0
  40. data/spec/system/api_ospf_spec.rb +111 -0
  41. data/spec/system/api_varp_interfaces_spec.rb +60 -0
  42. data/spec/system/api_varp_spec.rb +44 -0
  43. data/spec/system/rbeapi/api/dns_spec.rb +77 -0
  44. data/spec/system/rbeapi/api/interfaces_base_spec.rb +94 -0
  45. data/spec/system/rbeapi/api/interfaces_ethernet_spec.rb +135 -0
  46. data/spec/system/rbeapi/api/interfaces_portchannel_spec.rb +188 -0
  47. data/spec/system/rbeapi/api/interfaces_vxlan_spec.rb +115 -0
  48. data/spec/system/rbeapi/api/ipinterfaces_spec.rb +97 -0
  49. data/spec/system/rbeapi/api/logging_spec.rb +65 -0
  50. data/spec/system/rbeapi/api/mlag_interfaces_spec.rb +80 -0
  51. data/spec/system/rbeapi/api/mlag_spec.rb +94 -0
  52. data/spec/system/rbeapi/api/ntp_spec.rb +76 -0
  53. data/spec/system/rbeapi/api/snmp_spec.rb +68 -0
  54. data/spec/system/rbeapi/api/stp_instances_spec.rb +61 -0
  55. data/spec/system/rbeapi/api/stp_interfaces_spec.rb +71 -0
  56. data/spec/system/rbeapi/api/stp_spec.rb +57 -0
  57. data/spec/system/rbeapi/api/switchports_spec.rb +135 -0
  58. data/spec/system/rbeapi/api/system_spec.rb +38 -0
  59. data/spec/system/rbeapi/api/vlans_spec.rb +121 -0
  60. metadata +274 -0
@@ -0,0 +1,70 @@
1
+ #
2
+ # Copyright (c) 2014, 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
+
33
+ ##
34
+ # Rbeapi toplevel namespace
35
+ module Rbeapi
36
+ ##
37
+ # Utils module
38
+ module Utils
39
+
40
+ ##
41
+ # Iterates through a hash structure and converts all of the keys
42
+ # to symbols.
43
+ #
44
+ # @param [Hash] :value The hash structure to convert the keys
45
+ #
46
+ # @return [Hash] An updated hash structure with all keys converted to
47
+ # symboles
48
+ def self.transform_keys_to_symbols(value)
49
+ return value if not value.kind_of?(Hash)
50
+ hash = value.inject({}) do |hsh, (k,v)|
51
+ hsh[k.to_sym] = self.transform_keys_to_symbols(v)
52
+ hsh
53
+ end
54
+ hash
55
+ end
56
+
57
+ ##
58
+ # Returns a class object from a string capable of being instatiated
59
+ #
60
+ # @param [String] :name The name of the class to return a constant for
61
+ #
62
+ # @return [Object] Returns a a class object that can be instatiated
63
+ def self.class_from_string(name)
64
+ name.split('::').inject(Object) do |mod, cls|
65
+ mod.const_get(cls)
66
+ end
67
+ end
68
+ end
69
+ end
70
+
@@ -0,0 +1,37 @@
1
+ #
2
+ # Copyright (c) 2014, 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
+
33
+ # #
34
+ # Rbeapi toplevel namespace
35
+ module Rbeapi
36
+ VERSION = '0.1.0'
37
+ end
data/rbeapi.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rbeapi/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'rbeapi'
8
+ spec.version = Rbeapi::VERSION
9
+ spec.authors = ['Peter Sprygada']
10
+ spec.email = ['sprygada@arista.com']
11
+ spec.description = 'Arista eAPI Ruby Library'
12
+ spec.summary = 'This Gem library provides a Ruby interface '\
13
+ 'to the Arista EOS command API'
14
+ spec.homepage = 'https://github.com/arista-eosplus/rbeapi'
15
+ spec.license = 'New BSD'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0")
18
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'net_http_unix'
23
+ spec.add_dependency 'inifile'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.3'
26
+ spec.add_development_dependency 'yard'
27
+ spec.add_development_dependency 'redcarpet'
28
+ spec.add_development_dependency 'rake', '~> 10.1.0'
29
+ spec.add_development_dependency 'rspec', '~> 3.0.0'
30
+ spec.add_development_dependency 'rspec-mocks', '~> 3.0.0'
31
+ spec.add_development_dependency 'simplecov'
32
+ end
@@ -0,0 +1,5 @@
1
+ [connection:dut]
2
+ host: 192.168.1.16
3
+ username: eapi
4
+ password: password
5
+ transport: http
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_filter '/spec/'
6
+ add_filter '/.bundle/'
7
+ end
8
+
9
+ require 'pry'
10
+ require 'rbeapi'
11
+
12
+ dir = File.expand_path(File.dirname(__FILE__))
13
+ Dir["#{dir}/support/**/*.rb"].sort.each { |f| require f }
14
+
15
+ RSpec.configure do |config|
16
+ config.include FixtureHelpers
17
+
18
+ # rspec configuration
19
+ config.mock_with :rspec do |rspec_config|
20
+ rspec_config.syntax = :expect
21
+ end
22
+ end
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+
3
+ require 'pathname'
4
+ require 'yaml'
5
+ require 'json'
6
+
7
+ ##
8
+ # Fixtures implements a global container to store fixture data loaded from the
9
+ # filesystem.
10
+ class Fixtures
11
+ def self.[](name)
12
+ @fixtures[name]
13
+ end
14
+
15
+ def self.[]=(name, value)
16
+ @fixtures[name] = value
17
+ end
18
+
19
+ def self.clear
20
+ @fixtures = {}
21
+ end
22
+
23
+ clear
24
+
25
+ ##
26
+ # save an object and saves it as a fixture in the filesystem.
27
+ #
28
+ # @param [Symbol] key The fixture name without the `fixture_` prefix or
29
+ # `.json` suffix.
30
+ #
31
+ # @param [Object] obj The object to serialize to JSON and write to the
32
+ # fixture file.
33
+ #
34
+ # @option opts [String] :dir ('/path/to/fixtures') The fixtures directory,
35
+ # defaults to the full path of spec/fixtures/ relative to the root of the
36
+ # module.
37
+ def self.save(key, obj, opts = {})
38
+ dir = opts[:dir] || File.expand_path('../../fixtures', __FILE__)
39
+ file = Pathname.new(File.join(dir, "fixture_#{key}.yaml"))
40
+ fail ArgumentError, "Error, file #{file} exists" if file.exist?
41
+ File.open(file, 'w+') { |f| f.puts YAML.dump(obj) }
42
+ end
43
+ end
44
+
45
+ ##
46
+ # FixtureHelpers provides instance methods for RSpec test cases that aid in the
47
+ # loading and caching of fixture data.
48
+ module FixtureHelpers
49
+ ##
50
+ # fixture loads a JSON fixture from the spec/fixtures/ directory, prefixed
51
+ # with fixture_. Given the name 'foo' the file
52
+ # `spec/fixtures/fixture_foo.json` will be loaded and returned. This method
53
+ # is memoized across the life of the process.
54
+ #
55
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength,
56
+ # rubocop:disable Metrics/PerceivedComplexity
57
+ #
58
+ # @param [Symbol] key The fixture name without the `fixture_` prefix or
59
+ # `.json` suffix.
60
+ #
61
+ # @option opts [String] :dir ('/path/to/fixtures') The fixtures directory,
62
+ # defaults to the full path of spec/fixtures/ relative to the root of the
63
+ # module.
64
+ #
65
+ # @option opts [String] :format (:ruby) The format to return the fixture in,
66
+ # defaults to native Ruby objects. :json will return a JSON string.
67
+ def fixture(key, opts = { format: :ruby })
68
+ if opts[:format] == :ruby
69
+ memo = Fixtures[key]
70
+ return memo if memo
71
+ end
72
+ dir = opts[:dir] || fixture_dir
73
+
74
+ yaml = Pathname.new(File.join(dir, "fixture_#{key}.yaml"))
75
+ json = Pathname.new(File.join(dir, "fixture_#{key}.json"))
76
+
77
+ data = if yaml.exist?; then YAML.load(File.read(yaml))
78
+ elsif json.exist?; then JSON.load(File.read(json))
79
+ else fail "could not load YAML or JSON fixture #{key} "\
80
+ "tried:\n #{yaml}\n #{json}"
81
+ end
82
+
83
+ Fixtures[key] = data
84
+
85
+ case opts[:format]
86
+ when :ruby then data
87
+ when :json then JSON.pretty_generate(data)
88
+ when :yaml then YAML.dump(data)
89
+ else fail ArgumentError, "unknown format #{opts[:format].inspect}"
90
+ end
91
+ end
92
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
93
+ # rubocop:enable Metrics/PerceivedComplexity
94
+
95
+ ##
96
+ # fixture_dir returns the full path to the fixture directory
97
+ #
98
+ # @api public
99
+ #
100
+ # @return [String] the full path to the fixture directory
101
+ def fixture_dir
102
+ File.expand_path('../../fixtures', __FILE__)
103
+ end
104
+
105
+ ##
106
+ # fixture_file returns the full path to a file in the fixture directory
107
+ #
108
+ # @api public
109
+ #
110
+ # @return [String] the full path to the fixture file
111
+ def fixture_file(name)
112
+ File.join(fixture_dir, name)
113
+ end
114
+ end
@@ -0,0 +1,124 @@
1
+ RSpec.shared_examples 'a configurable entity' do |opts|
2
+
3
+ before(:each) do
4
+ allow(subject.node).to receive(:config)
5
+ end
6
+
7
+ applies_to = opts.fetch(:applies_to, [])
8
+ if applies_to.include?(opts[:args[0,2]]) or applies_to.empty?
9
+ context 'with node#config' do
10
+ [:create, :delete, :default].each do |action|
11
+ it "calls #{action} using #{opts[:args]}" do
12
+ expect(subject.node).to receive(:config).with(opts[action])
13
+ subject.send(action, *opts[:args])
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ RSpec.shared_examples 'a tristate attr' do |opts|
21
+
22
+ before(:each) do
23
+ allow(subject.node).to receive(:config)
24
+ end
25
+
26
+ applies_to = opts.fetch(:applies_to, [])
27
+ if applies_to.include?(opts[:args[0,2]]) or applies_to.empty?
28
+ context 'with node#config' do
29
+ it "configures with #{opts[:name]}" do
30
+ expect(subject.node).to receive(:config).with(opts[:config])
31
+ subject.send(opts[:name], *opts[:args], value: opts[:value])
32
+ end
33
+
34
+ it "negates with #{opts[:name]}" do
35
+ expect(subject.node).to receive(:config).with(opts[:negate])
36
+ subject.send(opts[:name], *opts[:args])
37
+ end
38
+
39
+ it "defaults" do
40
+ expect(subject.node).to receive(:config).with(opts[:default])
41
+ subject.send(opts[:name], *opts[:args], default: true)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ RSpec.shared_examples 'a settable attr' do |opts|
48
+
49
+ before(:each) do
50
+ allow(subject.node).to receive(:config)
51
+ end
52
+
53
+ context 'with node#config' do
54
+ it "configures with #{opts[:name]}" do
55
+ expect(subject.node).to receive(:config).with(opts[:config])
56
+ subject.send(opts[:name], *opts[:args])
57
+ end
58
+ end
59
+ end
60
+
61
+ RSpec.shared_examples 'a creatable entity' do |args, setup, block|
62
+
63
+ before(:each) do
64
+ subject.node.config(setup)
65
+ end
66
+
67
+ context 'with node#config' do
68
+ it "calls #create with #{args}" do
69
+ expect(subject.get_block(block)).to be_nil
70
+ subject.create(*args)
71
+ expect(subject.get_block(block)).not_to be_nil
72
+ end
73
+ end
74
+ end
75
+
76
+ RSpec.shared_examples 'a deletable entity' do |args, setup, block|
77
+
78
+ before(:each) do
79
+ subject.node.config(setup)
80
+ end
81
+
82
+ context 'with node#config' do
83
+ it "calls #delete with #{args}" do
84
+ expect(subject.get_block(block)).not_to be_nil
85
+ subject.delete(*args)
86
+ expect(subject.get_block(block)).to be_nil
87
+ end
88
+ end
89
+ end
90
+
91
+ RSpec.shared_examples 'a configurable attr' do |opts|
92
+
93
+ before(:each) do
94
+ subject.node.config(opts[:setup])
95
+ end
96
+
97
+ context 'with node#config' do
98
+ it "calls #{opts[:name]} with #{opts[:args]}" do
99
+ expect(subject.get_block(opts[:block])).to match(opts[:before])
100
+ subject.send(opts[:name], *opts[:args])
101
+ expect(subject.get_block(opts[:block])).to match(opts[:after])
102
+ end
103
+ end
104
+ end
105
+
106
+ RSpec.shared_examples 'single entity' do |opts|
107
+
108
+ describe 'match entity attributes from node' do
109
+ let(:entity) { subject.get(opts[:args]) }
110
+ opts[:entity].each do |key, value|
111
+ it "has #{key} with #{value}" do
112
+ binding.pry
113
+ expect(entity[key]).to eq(value)
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+
120
+
121
+
122
+
123
+
124
+
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ require 'rbeapi/client'
4
+ require 'rbeapi/api/ospf'
5
+
6
+ describe Rbeapi::Api::OspfInterfaces do
7
+ subject { described_class.new(node) }
8
+
9
+ let(:config) { Rbeapi::Client::Config.new(filename: get_fixture('dut.conf')) }
10
+ let(:node) { Rbeapi::Client.connect_to('veos02') }
11
+
12
+ describe '#get' do
13
+
14
+ before { node.config(['default interface Ethernet1', 'interface Ethernet1',
15
+ 'no switchport', 'ip address 99.99.99.99/24',
16
+ 'default interface Ethernet2']) }
17
+
18
+ it 'returns an ospf interface resource instance' do
19
+ expect(subject.get('Ethernet1')).not_to be_nil
20
+ end
21
+
22
+ it 'returns nil for a switchport interface' do
23
+ expect(subject.get('Ethernet2')).to be_nil
24
+ end
25
+ end
26
+
27
+ describe '#getall' do
28
+
29
+ before { node.config(['default interface Ethernet1', 'interface Ethernet1',
30
+ 'no switchport', 'ip address 99.99.99.99/24',
31
+ 'default interface Ethernet2']) }
32
+
33
+ it 'returns the ospf resource collection' do
34
+ expect(subject.getall).to be_a_kind_of(Hash)
35
+ end
36
+
37
+ it 'includes an instance of Ethernet1' do
38
+ expect(subject.getall).to include('Ethernet1')
39
+ end
40
+
41
+ it 'does not include an instance of Ethernet2' do
42
+ expect(subject.getall).not_to include('Ethernet2')
43
+ end
44
+
45
+ end
46
+
47
+ describe '#set_network_type' do
48
+
49
+ before { node.config(['default interface Ethernet1', 'interface Ethernet1',
50
+ 'no switchport', 'ip address 99.99.99.99/24']) }
51
+
52
+ it 'configures the ospf interface type to point-to-point' do
53
+ expect(subject.get('Ethernet1')['network_type']).to eq('broadcast')
54
+ expect(subject.set_network_type('Ethernet1', value: 'point-to-point')).to be_truthy
55
+ expect(subject.get('Ethernet1')['network_type']).to eq('point-to-point')
56
+ end
57
+ end
58
+ end