rbeapi 0.1.0

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