puppet_x_eos_eapi 0.2.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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +24 -0
- data/LICENSE.txt +202 -0
- data/README.md +87 -0
- data/Rakefile +1 -0
- data/lib/puppet_x/eos/autoload.rb +57 -0
- data/lib/puppet_x/eos/eapi.rb +259 -0
- data/lib/puppet_x/eos/module_base.rb +37 -0
- data/lib/puppet_x/eos/modules/daemon.rb +109 -0
- data/lib/puppet_x/eos/modules/extension.rb +167 -0
- data/lib/puppet_x/eos/modules/interface.rb +180 -0
- data/lib/puppet_x/eos/modules/ipinterface.rb +133 -0
- data/lib/puppet_x/eos/modules/mlag.rb +268 -0
- data/lib/puppet_x/eos/modules/ntp.rb +129 -0
- data/lib/puppet_x/eos/modules/ospf.rb +129 -0
- data/lib/puppet_x/eos/modules/portchannel.rb +277 -0
- data/lib/puppet_x/eos/modules/radius.rb +367 -0
- data/lib/puppet_x/eos/modules/snmp.rb +177 -0
- data/lib/puppet_x/eos/modules/switchport.rb +255 -0
- data/lib/puppet_x/eos/modules/system.rb +138 -0
- data/lib/puppet_x/eos/modules/tacacs.rb +302 -0
- data/lib/puppet_x/eos/modules/vlan.rb +179 -0
- data/lib/puppet_x/eos/modules/vxlan.rb +132 -0
- data/lib/puppet_x/eos/provider.rb +71 -0
- data/lib/puppet_x/eos/version.rb +41 -0
- data/lib/puppet_x/net_dev/eos_api.rb +1011 -0
- data/lib/puppet_x/net_dev/eos_api/common_methods.rb +27 -0
- data/lib/puppet_x/net_dev/eos_api/snmp_methods.rb +647 -0
- data/lib/puppet_x/net_dev/eos_api/version.rb +8 -0
- data/lib/puppet_x_eos_eapi.rb +4 -0
- data/puppet_x_eos_eapi.gemspec +31 -0
- data/spec/fixtures/fixture_all_portchannel_modes.json +8 -0
- data/spec/fixtures/fixture_all_portchannels_detailed.json +15 -0
- data/spec/fixtures/fixture_create_vlan_error.json +17 -0
- data/spec/fixtures/fixture_create_vlan_success.json +12 -0
- data/spec/fixtures/fixture_eapi_conf.yaml +4 -0
- data/spec/fixtures/fixture_enable_configure_vlan_3111_name_foo.json +14 -0
- data/spec/fixtures/fixture_enable_configure_vlan_foo_name_bar.json +19 -0
- data/spec/fixtures/fixture_get_snmp_communities_non_existent_acl.yaml +2 -0
- data/spec/fixtures/fixture_get_snmp_location_westeros.json +5 -0
- data/spec/fixtures/fixture_portchannel_min_links_1.json +8 -0
- data/spec/fixtures/fixture_portchannel_min_links_2.json +8 -0
- data/spec/fixtures/fixture_running_config.yaml +1 -0
- data/spec/fixtures/fixture_running_configuration_radius_configured.yaml +30 -0
- data/spec/fixtures/fixture_running_configuration_radius_default.yaml +29 -0
- data/spec/fixtures/fixture_running_configuration_radius_server_groups.yaml +38 -0
- data/spec/fixtures/fixture_running_configuration_radius_servers.yaml +34 -0
- data/spec/fixtures/fixture_running_configuration_tacacs_configured.yaml +38 -0
- data/spec/fixtures/fixture_running_configuration_tacacs_default.yaml +38 -0
- data/spec/fixtures/fixture_running_configuration_tacacs_groups.yaml +1 -0
- data/spec/fixtures/fixture_running_configuration_tacacs_groups_3.yaml +43 -0
- data/spec/fixtures/fixture_running_configuration_tacacs_servers.yaml +41 -0
- data/spec/fixtures/fixture_s4_show_etherchannel_detailed.json +9 -0
- data/spec/fixtures/fixture_show_flowcontrol_et1.json +5 -0
- data/spec/fixtures/fixture_show_interfaces.json +297 -0
- data/spec/fixtures/fixture_show_interfaces_switchport_format_text.json +9 -0
- data/spec/fixtures/fixture_show_port_channel_summary_2_lags.json +9 -0
- data/spec/fixtures/fixture_show_port_channel_summary_static.json +9 -0
- data/spec/fixtures/fixture_show_snmp_community.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_contact_empty.json +5 -0
- data/spec/fixtures/fixture_show_snmp_contact_name.json +5 -0
- data/spec/fixtures/fixture_show_snmp_disabled.json +5 -0
- data/spec/fixtures/fixture_show_snmp_enabled.json +5 -0
- data/spec/fixtures/fixture_show_snmp_host.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_host_duplicates.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_host_more_duplicates.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_location_empty.json +5 -0
- data/spec/fixtures/fixture_show_snmp_trap.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_user.yaml +2 -0
- data/spec/fixtures/fixture_show_snmp_user_raw_text.yaml +1 -0
- data/spec/fixtures/fixture_show_vlan.json +37 -0
- data/spec/fixtures/fixture_show_vlan_3110.json +18 -0
- data/spec/fixtures/fixture_show_vlan_4000.json +18 -0
- data/spec/fixtures/fixture_snmp_host_opts.yaml +11 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/fixtures.rb +104 -0
- data/spec/unit/puppet_x/eos/eapi_spec.rb +182 -0
- data/spec/unit/puppet_x/eos/module_base_spec.rb +26 -0
- data/spec/unit/puppet_x/eos/modules/daemon_spec.rb +110 -0
- data/spec/unit/puppet_x/eos/modules/extension_spec.rb +197 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/daemon_getall.json +3 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/extension_getall.json +28 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/hostname.json +6 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/interface_getall.json +509 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/ipinterface_getall.json +56 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/mlag_get.json +21 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/mlag_get_interfaces.json +18 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/ntp_get.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/ospf_instance_getall.json +58 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_get.json +54 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_getlacpmode.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_getmembers.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/portchannel_po1.json +7 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/snmp_get.json +14 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/switchport_get.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/switchport_get_et1.json +7 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/switchport_getall_interfaces.json +230 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/system_domain_list.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/system_domain_name.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/system_hostname.json +6 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/system_name_servers.json +5 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/vlan_getall.json +123 -0
- data/spec/unit/puppet_x/eos/modules/fixtures/vxlan_get.json +24 -0
- data/spec/unit/puppet_x/eos/modules/interface_spec.rb +281 -0
- data/spec/unit/puppet_x/eos/modules/ipinterface_spec.rb +143 -0
- data/spec/unit/puppet_x/eos/modules/mlag_spec.rb +349 -0
- data/spec/unit/puppet_x/eos/modules/ntp_spec.rb +136 -0
- data/spec/unit/puppet_x/eos/modules/ospf_spec.rb +143 -0
- data/spec/unit/puppet_x/eos/modules/portchannel_spec.rb +357 -0
- data/spec/unit/puppet_x/eos/modules/radius_spec.rb +509 -0
- data/spec/unit/puppet_x/eos/modules/snmp_spec.rb +202 -0
- data/spec/unit/puppet_x/eos/modules/switchport_get_et1.json +7 -0
- data/spec/unit/puppet_x/eos/modules/switchport_spec.rb +307 -0
- data/spec/unit/puppet_x/eos/modules/system_spec.rb +170 -0
- data/spec/unit/puppet_x/eos/modules/tacacs_spec.rb +448 -0
- data/spec/unit/puppet_x/eos/modules/vlan_spec.rb +244 -0
- data/spec/unit/puppet_x/eos/modules/vxlan_spec.rb +189 -0
- data/spec/unit/puppet_x/eos/provider_spec.rb +35 -0
- data/spec/unit/puppet_x/net_dev/eos_api/common_methods_spec.rb +34 -0
- data/spec/unit/puppet_x/net_dev/eos_api/snmp_methods_spec.rb +842 -0
- data/spec/unit/puppet_x/net_dev/eos_api_spec.rb +1000 -0
- metadata +369 -0
|
@@ -0,0 +1,132 @@
|
|
|
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
|
+
# Eos is the toplevel namespace for working with Arista EOS nodes
|
|
35
|
+
module PuppetX
|
|
36
|
+
##
|
|
37
|
+
# Eapi is module namesapce for working with the EOS command API
|
|
38
|
+
module Eos
|
|
39
|
+
##
|
|
40
|
+
# The Vxlan provides an instance for managing vxlan virtual tunnel
|
|
41
|
+
# interfaces in EOS
|
|
42
|
+
#
|
|
43
|
+
class Vxlan
|
|
44
|
+
def initialize(api)
|
|
45
|
+
@api = api
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
##
|
|
49
|
+
# Returns the vlan data for the provided id with the
|
|
50
|
+
# show vlan <id> command. If the id doesn't exist then
|
|
51
|
+
# nil is returned
|
|
52
|
+
#
|
|
53
|
+
#
|
|
54
|
+
# @return [nil, Hash<String, String|Hash|Array>] Hash describing the
|
|
55
|
+
# vlan configuration specified by id. If the id is not
|
|
56
|
+
# found then nil is returned
|
|
57
|
+
def get
|
|
58
|
+
@api.enable('show interfaces vxlan 1')
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Creates a new logical vxlan virtual interface in the running-config
|
|
63
|
+
#
|
|
64
|
+
# @return [Boolean] returns true if the command completed successfully
|
|
65
|
+
def create
|
|
66
|
+
@api.config('interface vxlan 1') == [{}]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
##
|
|
70
|
+
# Deletes an existing vxlan logical interface from the running-config
|
|
71
|
+
#
|
|
72
|
+
# @return [Boolean] always returns true
|
|
73
|
+
def delete
|
|
74
|
+
@api.config('no interface vxlan 1') == [{}]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
##
|
|
78
|
+
# Defaults an existing vxlan logical interface from the running-config)
|
|
79
|
+
#
|
|
80
|
+
# @return [Boolean] returns true if the command completed successfully
|
|
81
|
+
def default
|
|
82
|
+
@api.config('default interface vxlan 1') == [{}]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
##
|
|
86
|
+
# Configures the source-interface parameter for the Vxlan interface
|
|
87
|
+
#
|
|
88
|
+
# @param [Hash] opts The configuration parameters for the VLAN
|
|
89
|
+
# @option opts [string] :value The value to set the name to
|
|
90
|
+
# @option opts [Boolean] :default The value should be set to default
|
|
91
|
+
#
|
|
92
|
+
# @return [Boolean] returns true if the command completed successfully
|
|
93
|
+
def set_source_interface(opts = {})
|
|
94
|
+
value = opts[:value]
|
|
95
|
+
default = opts[:default] || false
|
|
96
|
+
|
|
97
|
+
cmds = ['interface vxlan 1']
|
|
98
|
+
case default
|
|
99
|
+
when true
|
|
100
|
+
cmds << 'default vxlan source-interface'
|
|
101
|
+
when false
|
|
102
|
+
cmds << (value.nil? ? 'no vxlan source-interface' : \
|
|
103
|
+
"vxlan source-interface #{value}")
|
|
104
|
+
end
|
|
105
|
+
@api.config(cmds) == [{}, {}]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
##
|
|
109
|
+
# Configures the multicast-group parameter for the Vxlan interface
|
|
110
|
+
#
|
|
111
|
+
# @param [Hash] opts The configuration parameters for the VLAN
|
|
112
|
+
# @option opts [string] :value The value to set the name to
|
|
113
|
+
# @option opts [Boolean] :default The value should be set to default
|
|
114
|
+
#
|
|
115
|
+
# @return [Boolean] returns true if the command completed successfully
|
|
116
|
+
def set_multicast_group(opts = {})
|
|
117
|
+
value = opts[:value]
|
|
118
|
+
default = opts[:default] || false
|
|
119
|
+
|
|
120
|
+
cmds = ['interface vxlan 1']
|
|
121
|
+
case default
|
|
122
|
+
when true
|
|
123
|
+
cmds << 'default vxlan multicast-group'
|
|
124
|
+
when false
|
|
125
|
+
cmds << (value.nil? ? 'no vxlan multicast-group' : \
|
|
126
|
+
"vxlan multicast-group #{value}")
|
|
127
|
+
end
|
|
128
|
+
@api.config(cmds) == [{}, {}]
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
require 'puppet_x/eos/eapi'
|
|
33
|
+
require 'pathname'
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
# PuppetX namespace
|
|
37
|
+
module PuppetX
|
|
38
|
+
##
|
|
39
|
+
# Eos namesapece
|
|
40
|
+
module Eos
|
|
41
|
+
##
|
|
42
|
+
# EapiProviderMixin module
|
|
43
|
+
module EapiProviderMixin
|
|
44
|
+
def prefetch(resources)
|
|
45
|
+
provider_hash = instances.each_with_object({}) do |provider, hsh|
|
|
46
|
+
hsh[provider.name] = provider
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
resources.each_pair do |name, resource|
|
|
50
|
+
resource.provider = provider_hash[name] if provider_hash[name]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
##
|
|
55
|
+
# conf loads a YAML file from '/mnt/flash/eapi.conf' if it exists. If it
|
|
56
|
+
# does not exist an empty hash is returned.
|
|
57
|
+
def conf
|
|
58
|
+
config_file = Pathname.new('/mnt/flash/eapi.conf')
|
|
59
|
+
if config_file.exist?
|
|
60
|
+
YAML.load_file(config_file.to_s)
|
|
61
|
+
else
|
|
62
|
+
Hash.new
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def eapi
|
|
67
|
+
@eapi ||= PuppetX::Eos::Eapi.new(conf)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
# PuppetX namespace
|
|
35
|
+
module PuppetX
|
|
36
|
+
##
|
|
37
|
+
# Arista EOS namespace
|
|
38
|
+
module Eos
|
|
39
|
+
VERSION = '0.2.0'
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,1011 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'net_http_unix'
|
|
4
|
+
require 'puppet_x/net_dev/eos_api/common_methods'
|
|
5
|
+
require 'puppet_x/net_dev/eos_api/snmp_methods'
|
|
6
|
+
require 'securerandom'
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
# PuppetX is where utility extensions live.
|
|
10
|
+
module PuppetX
|
|
11
|
+
##
|
|
12
|
+
# NetDev is the module namespace for puppet supported
|
|
13
|
+
module NetDev
|
|
14
|
+
## ApiError is raised on REST API errors
|
|
15
|
+
class ApiError < Exception; end
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# EosApi provides utility methods to interact with the eAPI using JSON-RPC.
|
|
19
|
+
# The API may be accessed over a normal TCP connection using the address
|
|
20
|
+
# and port settings, or a Unix domain socket using a `unix://...` address.
|
|
21
|
+
#
|
|
22
|
+
# @example Get all VLAN identifiers as strings
|
|
23
|
+
# >> api = EosApi.new(address: 'unix:///var/lib/eapi.sock')
|
|
24
|
+
# >> vlans = api.all_vlans
|
|
25
|
+
# >> vlans.keys
|
|
26
|
+
# => ['1', '3110']
|
|
27
|
+
class EosApi # rubocop:disable Metrics/ClassLength
|
|
28
|
+
# IP address or hostname of the REST api
|
|
29
|
+
attr_reader :address
|
|
30
|
+
# TCP port of the REST api
|
|
31
|
+
attr_reader :port
|
|
32
|
+
# API username
|
|
33
|
+
attr_reader :username
|
|
34
|
+
# API password
|
|
35
|
+
attr_reader :password
|
|
36
|
+
|
|
37
|
+
# Include type specific methods, broken out for clear organization.
|
|
38
|
+
include CommonMethods
|
|
39
|
+
include SnmpMethods
|
|
40
|
+
|
|
41
|
+
##
|
|
42
|
+
# initialize an API instance. The API will communicate with the HTTP
|
|
43
|
+
# server over TCP or a Unix Domain Socket. If a unix domain socket is
|
|
44
|
+
# being used then the address parameter should be set to the socket path.
|
|
45
|
+
# The port, username, and password are not necessary.
|
|
46
|
+
#
|
|
47
|
+
# @option opts [String] :address The address to connect to the HTTP
|
|
48
|
+
# server. This can be a hostname, address or the full path to a unix
|
|
49
|
+
# domain socket in the form of `unix:///path/to/socket`.
|
|
50
|
+
#
|
|
51
|
+
# @option opts [Fixnum] :port The TCP port for an IP connection to the
|
|
52
|
+
# HTTP API server.
|
|
53
|
+
#
|
|
54
|
+
# @option opts [String] :username ('admin') The username to log into the
|
|
55
|
+
# API server when using TCP/IP HTTP API connections. This option is
|
|
56
|
+
# not necessary when using a unix:// socket connection.
|
|
57
|
+
#
|
|
58
|
+
# @option opts [String] :password The password to use to log into the API
|
|
59
|
+
# server.
|
|
60
|
+
#
|
|
61
|
+
# @return [PuppetX::NetDev::EosApi]
|
|
62
|
+
#
|
|
63
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
64
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
65
|
+
def initialize(opts = {})
|
|
66
|
+
@address = opts[:address] || ENV['EOS_HOSTNAME'] || 'unix:///var/run/command-api.sock'
|
|
67
|
+
@port = opts[:port] || ENV['EOS_PORT'] || 80
|
|
68
|
+
@username = opts[:username] || ENV['EOS_USERNAME'] || 'admin'
|
|
69
|
+
@password = opts[:password] || ENV['EOS_PASSWORD'] || 'puppet'
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
##
|
|
73
|
+
# vlan returns data about a specific VLAN identified by the VLAN ID
|
|
74
|
+
# number. This API call maps roughly to the `show vlan <id>` command.
|
|
75
|
+
# This method returns nil if no VLAN was found matching the ID provided.
|
|
76
|
+
#
|
|
77
|
+
# @param [Fixnum] id The VLAN id to obtain information about.
|
|
78
|
+
#
|
|
79
|
+
# @api public
|
|
80
|
+
#
|
|
81
|
+
# @return [nil,Hash<String,Hash>] Hash describing the VLAN attributes, or
|
|
82
|
+
# nil if no vlan was found matching the id provided. The format of
|
|
83
|
+
# this hash matches the format of {all_vlans}
|
|
84
|
+
def vlan(id)
|
|
85
|
+
result = eapi_action("show vlan #{id}", 'list vlans')
|
|
86
|
+
result.first['vlans'] if result
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
##
|
|
90
|
+
# vlan_create creates a VLAN that does not yet exist on the target
|
|
91
|
+
# device.
|
|
92
|
+
#
|
|
93
|
+
# @param [Fixnum] id The VLAN id to create
|
|
94
|
+
#
|
|
95
|
+
# @api public
|
|
96
|
+
#
|
|
97
|
+
# @return [Boolean] true if the vlan was created
|
|
98
|
+
def vlan_create(id)
|
|
99
|
+
cmds = ['enable', 'configure', "vlan #{id}"]
|
|
100
|
+
eapi_action(cmds, "create vlan #{id}")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
##
|
|
104
|
+
# vlan_destroy destroys a vlan
|
|
105
|
+
#
|
|
106
|
+
# @param [Integer] id The VLAN ID to destroy
|
|
107
|
+
#
|
|
108
|
+
# @api public
|
|
109
|
+
def vlan_destroy(id)
|
|
110
|
+
cmds = ['enable', 'configure', "no vlan #{id}"]
|
|
111
|
+
eapi_action(cmds, "destroy vlan #{id}")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
##
|
|
115
|
+
# channel_group_destroy destroys a port channel group.
|
|
116
|
+
#
|
|
117
|
+
# @param [String] name The port channel name, e.g 'Port-Channel3'
|
|
118
|
+
#
|
|
119
|
+
# @api public
|
|
120
|
+
def channel_group_destroy(name)
|
|
121
|
+
# Need to remove all interfaces from the channel group.
|
|
122
|
+
port_channels = all_portchannels_detailed
|
|
123
|
+
channel_group = port_channels[name]
|
|
124
|
+
unless channel_group
|
|
125
|
+
msg = "#{name} is not in #{port_channels.keys.inspect}"
|
|
126
|
+
fail ArgumentError, msg
|
|
127
|
+
end
|
|
128
|
+
interfaces = channel_group['ports']
|
|
129
|
+
interfaces.each { |iface| interface_unset_channel_group(iface) }
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
##
|
|
133
|
+
# interface_unset_channel_group removes a specific interface from all
|
|
134
|
+
# channel groups.
|
|
135
|
+
#
|
|
136
|
+
# @param [String] interface The interface name to remove from its
|
|
137
|
+
# associated channel group, e.g. 'Ethernet1'
|
|
138
|
+
#
|
|
139
|
+
# @api public
|
|
140
|
+
def interface_unset_channel_group(interface)
|
|
141
|
+
cmds = %w(enable configure) << "interface #{interface}"
|
|
142
|
+
cmds << 'no channel-group'
|
|
143
|
+
eapi_action(cmds, "remove #{interface} from channel group")
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
##
|
|
147
|
+
# interface_set_channel_group configures an interface to be a member of a
|
|
148
|
+
# specified channel group ID.
|
|
149
|
+
#
|
|
150
|
+
# @param [String] interface The interface name to add to the channel
|
|
151
|
+
# group, e.g. 'Ethernet1'.
|
|
152
|
+
#
|
|
153
|
+
# @option opts [Fixnum] :group The group ID the interface will become a
|
|
154
|
+
# member of, e.g. 3.
|
|
155
|
+
#
|
|
156
|
+
# @option opts [Symbol] :mode (:active, :passive, :disabled) The LACP
|
|
157
|
+
# operating mode of the interface. Note, the only way to change the
|
|
158
|
+
# LACP mode is to delete the channel group and re-create the channel
|
|
159
|
+
# group.
|
|
160
|
+
#
|
|
161
|
+
# @api public
|
|
162
|
+
def interface_set_channel_group(interface, opts)
|
|
163
|
+
channel_group = opts[:group]
|
|
164
|
+
mode = case opts[:mode]
|
|
165
|
+
when :active, :passive then opts[:mode]
|
|
166
|
+
when :disabled then :on
|
|
167
|
+
else fail ArgumentError, "Unknown LACP mode #{opts[:mode]}"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
cmd = %w(enable configure) << "interface #{interface}"
|
|
171
|
+
cmd << "channel-group #{channel_group} mode #{mode}"
|
|
172
|
+
msg = "join #{interface} to channel group #{channel_group}"
|
|
173
|
+
eapi_action(cmd, msg)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
##
|
|
177
|
+
# port_channel_destroy destroys a port channel interface and removes all
|
|
178
|
+
# interfaces from the channel group.
|
|
179
|
+
#
|
|
180
|
+
# @param [String] name The name of the port channel interface, e.g
|
|
181
|
+
# 'Port-Channel3'
|
|
182
|
+
#
|
|
183
|
+
# @api public
|
|
184
|
+
def port_channel_destroy(name)
|
|
185
|
+
cmds = %w(enable configure) << "no interface #{name}"
|
|
186
|
+
eapi_action(cmds, "remove #{name}")
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
##
|
|
190
|
+
# channel_group_create creates a channel group and associated port
|
|
191
|
+
# channel interface if the interface does not already exist.
|
|
192
|
+
#
|
|
193
|
+
# @param [String] name The name of the port channel interface, e.g.
|
|
194
|
+
# 'Port-Channel3'.
|
|
195
|
+
#
|
|
196
|
+
# @option opts [Symbol] :mode (:active, :passive, :disabled) The LACP
|
|
197
|
+
# operating mode of the interface. Note, the only way to change the
|
|
198
|
+
# LACP mode is to delete the channel group and re-create the channel
|
|
199
|
+
# group.
|
|
200
|
+
#
|
|
201
|
+
# @option opts [Symbol] :interfaces (['Ethernet1', 'Ethernet2']) The
|
|
202
|
+
# member interfaces of the channel group.
|
|
203
|
+
#
|
|
204
|
+
# @api public
|
|
205
|
+
def channel_group_create(name, opts)
|
|
206
|
+
channel_group = name.scan(/\d+/).first.to_i
|
|
207
|
+
interfaces = [*opts[:interfaces]]
|
|
208
|
+
if interfaces.empty?
|
|
209
|
+
fail ArgumentError, 'Cannot create a channel group with no interfaces'
|
|
210
|
+
end
|
|
211
|
+
interfaces.each do |interface|
|
|
212
|
+
set_opts = { mode: opts[:mode], group: channel_group }
|
|
213
|
+
interface_set_channel_group(interface, set_opts)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
##
|
|
218
|
+
# set_vlan_name assigns a name to a vlan
|
|
219
|
+
#
|
|
220
|
+
# @param [Integer] id The vlan ID
|
|
221
|
+
#
|
|
222
|
+
# @param [String] name The vlan name
|
|
223
|
+
#
|
|
224
|
+
# @api public
|
|
225
|
+
def set_vlan_name(id, name)
|
|
226
|
+
cmds = ['enable', 'configure', "vlan #{id}"] << "name #{name}"
|
|
227
|
+
eapi_action(cmds, "set vlan #{id} name to #{name}")
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
##
|
|
231
|
+
# set_vlan_state set a vlan to the state specified
|
|
232
|
+
#
|
|
233
|
+
# @param [Integer] id The vlan ID
|
|
234
|
+
#
|
|
235
|
+
# @param [String] state The state of the vlan, e.g. 'active' or
|
|
236
|
+
# 'suspend'
|
|
237
|
+
#
|
|
238
|
+
# @api public
|
|
239
|
+
def set_vlan_state(id, state)
|
|
240
|
+
cmds = ['enable', 'configure', "vlan #{id}"] << "state #{state}"
|
|
241
|
+
eapi_action(cmds, "set vlan #{id} state to #{state}")
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
##
|
|
245
|
+
# all_vlans returns a hash of all vlans
|
|
246
|
+
#
|
|
247
|
+
# @example List all vlans
|
|
248
|
+
# api.all_vlans
|
|
249
|
+
# => {
|
|
250
|
+
# "1"=>{
|
|
251
|
+
# "status"=>"active",
|
|
252
|
+
# "name"=>"default",
|
|
253
|
+
# "interfaces"=>{
|
|
254
|
+
# "Ethernet2"=>{"privatePromoted"=>false},
|
|
255
|
+
# "Ethernet3"=>{"privatePromoted"=>false},
|
|
256
|
+
# "Ethernet1"=>{"privatePromoted"=>false},
|
|
257
|
+
# "Ethernet4"=>{"privatePromoted"=>false}},
|
|
258
|
+
# "dynamic"=>false},
|
|
259
|
+
# "3110"=>{
|
|
260
|
+
# "status"=>"active",
|
|
261
|
+
# "name"=>"VLAN3110",
|
|
262
|
+
# "interfaces"=>{},
|
|
263
|
+
# "dynamic"=>false}}
|
|
264
|
+
#
|
|
265
|
+
# @api public
|
|
266
|
+
#
|
|
267
|
+
# @return [Hash<String,Hash>]
|
|
268
|
+
def all_vlans
|
|
269
|
+
result = eapi_action('show vlan', 'list all vlans')
|
|
270
|
+
result.first['vlans']
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
##
|
|
274
|
+
# all_portchannels returns a hash of all port channels based on multiple
|
|
275
|
+
# sources of data from the API.
|
|
276
|
+
#
|
|
277
|
+
# @api public
|
|
278
|
+
#
|
|
279
|
+
# @return [Hash<String,Hash>] where the key is the port channel name,
|
|
280
|
+
# e.g. 'Port-Channel10'
|
|
281
|
+
def all_portchannels
|
|
282
|
+
detailed = all_portchannels_detailed
|
|
283
|
+
modes = all_portchannel_modes
|
|
284
|
+
# Merge the two
|
|
285
|
+
detailed.each_with_object(Hash.new) do |(name, attr), hsh|
|
|
286
|
+
hsh[name] = modes[name] ? attr.merge(modes[name]) : attr
|
|
287
|
+
hsh[name]['minimum_links'] = portchannel_min_links(name)
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
##
|
|
292
|
+
# portchannel_min_links takes the name of a Port Channel interface and
|
|
293
|
+
# obtains the currently configured min-links value by parsing the text of
|
|
294
|
+
# the running configuration.
|
|
295
|
+
#
|
|
296
|
+
# @api private
|
|
297
|
+
#
|
|
298
|
+
# @return [Fixnum] the minimum number of links for the channel group to
|
|
299
|
+
# become active.
|
|
300
|
+
def portchannel_min_links(name)
|
|
301
|
+
api_commands = ['enable', "show running-config interfaces #{name}"]
|
|
302
|
+
result = eapi_action(api_commands,
|
|
303
|
+
'obtain port channel min links value',
|
|
304
|
+
format: 'text')
|
|
305
|
+
text = result[1]['output'] # skip over the enable command output.
|
|
306
|
+
parse_min_links(text)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
##
|
|
310
|
+
# set_portchannel_min_links Configures the minimum links value for a
|
|
311
|
+
# channel group.
|
|
312
|
+
#
|
|
313
|
+
# @param [String] name The port channel name, e.g 'Port-Channel4'.
|
|
314
|
+
#
|
|
315
|
+
# @param [Fixnum] min_links The minimum number of active links for the
|
|
316
|
+
# channel group to be active.
|
|
317
|
+
#
|
|
318
|
+
# @api public
|
|
319
|
+
def set_portchannel_min_links(name, min_links)
|
|
320
|
+
cmd = %w(enable configure)
|
|
321
|
+
cmd << "interface #{name}"
|
|
322
|
+
cmd << "port-channel min-links #{min_links}"
|
|
323
|
+
eapi_action(cmd, 'set port-channel min links')
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
##
|
|
327
|
+
# parse_min_links takes the text from the `show running-config interfaces
|
|
328
|
+
# Port-ChannelX` API command and parses out the currently configured
|
|
329
|
+
# number of minimum links. If there is no min-links value we (safely)
|
|
330
|
+
# assume it is configured to 0. Example output is:
|
|
331
|
+
#
|
|
332
|
+
# interface Port-Channel4
|
|
333
|
+
# description Office Backbone
|
|
334
|
+
# port-channel min-links 2
|
|
335
|
+
#
|
|
336
|
+
# @param [String] text The raw text output from the API.
|
|
337
|
+
#
|
|
338
|
+
# @api private
|
|
339
|
+
#
|
|
340
|
+
# @return [Fixnum] the number of minimum links
|
|
341
|
+
def parse_min_links(text)
|
|
342
|
+
re = /min-links\s+(\d+)/m
|
|
343
|
+
mdata = re.match(text)
|
|
344
|
+
mdata ? mdata[1].to_i : 0
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
##
|
|
348
|
+
# all_portchannels_detailed returns a hash of all port channels based on
|
|
349
|
+
# the `show etherchannel detailed` command.
|
|
350
|
+
#
|
|
351
|
+
# @api private
|
|
352
|
+
#
|
|
353
|
+
# @return [Hash<String,Hash>] where the key is the port channel name,
|
|
354
|
+
# e.g. 'Port-Channel10'
|
|
355
|
+
def all_portchannels_detailed
|
|
356
|
+
# JSON format is not supported in EOS 4.13.7M so use text format
|
|
357
|
+
result = eapi_action('show etherchannel detailed', 'list port channels',
|
|
358
|
+
format: 'text')
|
|
359
|
+
text = result.first['output']
|
|
360
|
+
parse_portchannels(text)
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
##
|
|
364
|
+
# all_portchannel_modes returns a hash of each of the port channel LACP
|
|
365
|
+
# modes. This method could be merged with the data from the
|
|
366
|
+
# all_portchannels method.
|
|
367
|
+
#
|
|
368
|
+
# @api private
|
|
369
|
+
#
|
|
370
|
+
# @return [Hash<String,Hash>] where the key is the port channel name,
|
|
371
|
+
# e.g. 'Port-Channel10'
|
|
372
|
+
def all_portchannel_modes
|
|
373
|
+
# JSON format is not supported in EOS 4.13.7M so use text format
|
|
374
|
+
result = eapi_action('show port-channel summary', 'get lag modes',
|
|
375
|
+
format: 'text')
|
|
376
|
+
text = result.first['output']
|
|
377
|
+
parse_portchannel_modes(text)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
##
|
|
381
|
+
# Parse the portchannel modes from the text of the `show port-channel
|
|
382
|
+
# summary` command. The following is an example of two channel groups,
|
|
383
|
+
# one static, one active.
|
|
384
|
+
#
|
|
385
|
+
# rubocop:disable Metrics/LineLength, Metrics/MethodLength, Style/TrailingWhitespace
|
|
386
|
+
#
|
|
387
|
+
# Flags
|
|
388
|
+
# ------------------------ ---------------------------- -------------------------
|
|
389
|
+
# a - LACP Active p - LACP Passive * - static fallback
|
|
390
|
+
# F - Fallback enabled f - Fallback configured ^ - individual fallback
|
|
391
|
+
# U - In Use D - Down
|
|
392
|
+
# + - In-Sync - - Out-of-Sync i - incompatible with agg
|
|
393
|
+
# P - bundled in Po s - suspended G - Aggregable
|
|
394
|
+
# I - Individual S - ShortTimeout w - wait for agg
|
|
395
|
+
#
|
|
396
|
+
# Number of channels in use: 1
|
|
397
|
+
# Number of aggregators:1
|
|
398
|
+
#
|
|
399
|
+
# Port-Channel Protocol Ports
|
|
400
|
+
# ------------------ -------------- ----------------
|
|
401
|
+
# Po3(U) Static Et1(D) Et2(P)
|
|
402
|
+
# Po4(D) LACP(a) Et3(G-) Et4(G-)
|
|
403
|
+
#
|
|
404
|
+
# @api private
|
|
405
|
+
#
|
|
406
|
+
# @return [Hash<String,Hash>] where the key is the port channel name,
|
|
407
|
+
# e.g. 'Port-Channel10'
|
|
408
|
+
def parse_portchannel_modes(text)
|
|
409
|
+
lines = text.lines.each_with_object(Array.new) do |v, ary|
|
|
410
|
+
ary << v.chomp if /^\s*Po\d/.match(v)
|
|
411
|
+
end
|
|
412
|
+
lines.each_with_object(Hash.new) do |line, hsh|
|
|
413
|
+
mdata = /^\s+Po(\d+).*?\s+([a-zA-Z()0-9_-]+)/.match(line)
|
|
414
|
+
idx = mdata[1]
|
|
415
|
+
protocol = mdata[2]
|
|
416
|
+
mode = case protocol
|
|
417
|
+
when 'Static' then :disabled
|
|
418
|
+
when /LACP/
|
|
419
|
+
flags = /\((.*?)\)/.match(protocol)[1]
|
|
420
|
+
if flags.include? 'p' then :passive
|
|
421
|
+
elsif flags.include? 'a' then :active
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
hsh["Port-Channel#{idx}"] = { 'mode' => mode }
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
##
|
|
429
|
+
# get_flowcontrol obtains the configured flow_control send and receive
|
|
430
|
+
# values from the target device.
|
|
431
|
+
#
|
|
432
|
+
# @param [String] name The interface name, e.g. 'Ethernet1'
|
|
433
|
+
#
|
|
434
|
+
# @api public
|
|
435
|
+
#
|
|
436
|
+
# @return [Hash<Symbol,String>] e.g. { send: 'on', receive: 'off' }
|
|
437
|
+
def get_flowcontrol(name)
|
|
438
|
+
cmd = "show flowcontrol interface #{name}"
|
|
439
|
+
result = eapi_action(cmd, 'get flowcontrol config', format: 'text')
|
|
440
|
+
text = result.first['output']
|
|
441
|
+
parse_flowcontrol_single(text)
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
##
|
|
445
|
+
# parse_flowcontrol_single parses the text output of the `show
|
|
446
|
+
# flowcontrol <interface>` command where there is a single entry for the
|
|
447
|
+
# named interface.
|
|
448
|
+
#
|
|
449
|
+
# Port Send FlowControl Receive FlowControl RxPause TxPause
|
|
450
|
+
# admin oper admin oper
|
|
451
|
+
# ---------- -------- -------- -------- -------- ------------- -------------
|
|
452
|
+
# Et1 off unknown off unknown 0 0
|
|
453
|
+
#
|
|
454
|
+
# @param [String] text The text to parse
|
|
455
|
+
#
|
|
456
|
+
# @api private
|
|
457
|
+
#
|
|
458
|
+
# @return [Hash<Symbol,String>] e.g. { send: 'on', receive: 'off' }
|
|
459
|
+
def parse_flowcontrol_single(text)
|
|
460
|
+
re = /----\n(.*?)\s+(.*?)\s+.*?\s+(.*?)\s+.*\n/m
|
|
461
|
+
mdata = re.match(text)
|
|
462
|
+
if mdata
|
|
463
|
+
{ send: mdata[2], receive: mdata[3] }
|
|
464
|
+
else
|
|
465
|
+
fail ArgumentError, 'could not parse flowcontrol'
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
##
|
|
470
|
+
# set_flowcontrol_send Configures a specific interface's flow control
|
|
471
|
+
# send value.
|
|
472
|
+
#
|
|
473
|
+
# @param [String] name The name of the interface to configure, e.g.
|
|
474
|
+
# 'Ethernet1'
|
|
475
|
+
#
|
|
476
|
+
# @param [Symbol] value the value to configure, e.g. `:on`, `:off`
|
|
477
|
+
#
|
|
478
|
+
# @api public
|
|
479
|
+
def set_flowcontrol_send(name, value)
|
|
480
|
+
cmd = %w(enable configure) << "interface #{name}"
|
|
481
|
+
cmd << "flowcontrol send #{value}"
|
|
482
|
+
eapi_action(cmd, 'configure flowcontrol send')
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
##
|
|
486
|
+
# set_flowcontrol_recv Configures a specific interface's flow control
|
|
487
|
+
# receive value.
|
|
488
|
+
#
|
|
489
|
+
# @param [String] name The name of the interface to configure, e.g.
|
|
490
|
+
# 'Ethernet1'
|
|
491
|
+
#
|
|
492
|
+
# @param [Symbol] value the value to configure, e.g. `:on`, `:off`
|
|
493
|
+
#
|
|
494
|
+
# @api public
|
|
495
|
+
def set_flowcontrol_recv(name, value)
|
|
496
|
+
cmd = %w(enable configure) << "interface #{name}"
|
|
497
|
+
cmd << "flowcontrol receive #{value}"
|
|
498
|
+
eapi_action(cmd, 'configure flowcontrol receive')
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
##
|
|
502
|
+
# all_interfaces returns a hash of all interfaces
|
|
503
|
+
#
|
|
504
|
+
# @api public
|
|
505
|
+
#
|
|
506
|
+
# @return [Hash<String,Hash>] where the key is the interface name, e.g.
|
|
507
|
+
# 'Management1'
|
|
508
|
+
def all_interfaces
|
|
509
|
+
result = eapi_action('show interfaces', 'list all interfaces')
|
|
510
|
+
result.first['interfaces']
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
##
|
|
514
|
+
# set_interface_state enables or disables a network interface
|
|
515
|
+
#
|
|
516
|
+
# @param [String] name The interface name, e.g. 'Ethernet1'
|
|
517
|
+
#
|
|
518
|
+
# @param [String] state The interface state, e.g. 'no shutdown' or
|
|
519
|
+
# 'shutdown'
|
|
520
|
+
#
|
|
521
|
+
# @api public
|
|
522
|
+
def set_interface_state(name, state)
|
|
523
|
+
cmd = %w(enable configure) << "interface #{name}" << state
|
|
524
|
+
eapi_action(cmd, "set interface #{name} state to #{state}")
|
|
525
|
+
end
|
|
526
|
+
|
|
527
|
+
##
|
|
528
|
+
# set_interface_description configures the description string for an
|
|
529
|
+
# interface.
|
|
530
|
+
#
|
|
531
|
+
# @param [String] name The interface name, e.g. 'Ethernet1'
|
|
532
|
+
#
|
|
533
|
+
# @param [String] description The description to assign the interface.
|
|
534
|
+
#
|
|
535
|
+
# @api public
|
|
536
|
+
def set_interface_description(name, description)
|
|
537
|
+
cmd = %w(enable configure) << "interface #{name}"
|
|
538
|
+
cmd << "description #{description}"
|
|
539
|
+
eapi_action(cmd, "set interface #{name} description to #{description}")
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
##
|
|
543
|
+
# set_interface_speed enable a network interface
|
|
544
|
+
#
|
|
545
|
+
# @param [String] name The interface name, e.g. 'Ethernet1'
|
|
546
|
+
#
|
|
547
|
+
# @param [String] speed The interface state, e.g. '1000full' or
|
|
548
|
+
# '40gfull'
|
|
549
|
+
#
|
|
550
|
+
# @api public
|
|
551
|
+
def set_interface_speed(name, speed)
|
|
552
|
+
cmd = %w(enable configure) << "interface #{name}"
|
|
553
|
+
cmd << "speed forced #{speed}"
|
|
554
|
+
eapi_action(cmd, "set interface #{name} speed to #{speed}")
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
##
|
|
558
|
+
# set_interface_mtu configures the interface MTU
|
|
559
|
+
#
|
|
560
|
+
# @param [String] name The interface name, e.g. 'Ethernet1'
|
|
561
|
+
#
|
|
562
|
+
# @param [Fixnum] mtu The interface mtu, e.g. 9000
|
|
563
|
+
#
|
|
564
|
+
# @api public
|
|
565
|
+
def set_interface_mtu(name, mtu)
|
|
566
|
+
cmd = %w(enable configure) << "interface #{name}"
|
|
567
|
+
cmd << "mtu #{mtu}"
|
|
568
|
+
eapi_action(cmd, "set interface #{name} mtu to #{mtu}")
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
##
|
|
572
|
+
# format_error takes the value of the 'error' key from the EOS API
|
|
573
|
+
# response and formats the error strings into a string suitable for error
|
|
574
|
+
# messages.
|
|
575
|
+
#
|
|
576
|
+
# @param [Array<Hash>] data Array of data from the API response, this
|
|
577
|
+
# will be lcoated in the sub-key api_response['error']['data']
|
|
578
|
+
#
|
|
579
|
+
# @api private
|
|
580
|
+
#
|
|
581
|
+
# @return [String] the human readable error message
|
|
582
|
+
def format_error(data)
|
|
583
|
+
if data
|
|
584
|
+
data.each_with_object([]) do |i, ary|
|
|
585
|
+
ary.push(*i['errors']) if i['errors']
|
|
586
|
+
end.join(', ')
|
|
587
|
+
else
|
|
588
|
+
'unknown error'
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
##
|
|
593
|
+
# http returns a memoized HTTP client instance conforming to the
|
|
594
|
+
# Net::HTTP interface.
|
|
595
|
+
#
|
|
596
|
+
# @api public
|
|
597
|
+
#
|
|
598
|
+
# @return [NetX::HttpUnix]
|
|
599
|
+
def http
|
|
600
|
+
@http ||= NetX::HTTPUnix.new(address, port)
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
##
|
|
604
|
+
# format_command takes an EOS command as a string and returns the
|
|
605
|
+
# appropriate data structure for use with the EOS REST API.
|
|
606
|
+
#
|
|
607
|
+
# @param [String, Array<String>] command The command to execute on the
|
|
608
|
+
# switch, e.g. 'show vlan' or ['show vlan 1', 'show vlan 2'].
|
|
609
|
+
#
|
|
610
|
+
# @option opts [String] :id The identifier for this request. If omitted,
|
|
611
|
+
# a unique identifier will be generated.
|
|
612
|
+
#
|
|
613
|
+
# @option opts [String] :format ('json') The desired format of the
|
|
614
|
+
# response, e.g. 'text' or 'json'. Defaults to 'json' if not provided.
|
|
615
|
+
#
|
|
616
|
+
# @api private
|
|
617
|
+
#
|
|
618
|
+
# @return [String] The JSON string suitable for use with HTTP POST API
|
|
619
|
+
# calls to the EOS API.
|
|
620
|
+
def format_command(command, options = {})
|
|
621
|
+
cmds = [*command]
|
|
622
|
+
req_id = options[:id].nil? ? SecureRandom.uuid : options[:id]
|
|
623
|
+
format = options[:format].nil? ? 'json' : options[:format]
|
|
624
|
+
params = { 'version' => 1, 'cmds' => cmds, 'format' => format }
|
|
625
|
+
request = {
|
|
626
|
+
'jsonrpc' => '2.0', 'method' => 'runCmds',
|
|
627
|
+
'params' => params, 'id' => req_id
|
|
628
|
+
}
|
|
629
|
+
JSON.dump(request)
|
|
630
|
+
end
|
|
631
|
+
private :format_command
|
|
632
|
+
|
|
633
|
+
##
|
|
634
|
+
# parse_portchannels accepts the text output of the `show etherchannel
|
|
635
|
+
# detailed` command and parses the text into structured data with the
|
|
636
|
+
# portchannel names as keys and portchannel attributes as key/values in a
|
|
637
|
+
# hash.
|
|
638
|
+
#
|
|
639
|
+
# @param [String] text The text output to parse.
|
|
640
|
+
#
|
|
641
|
+
# @api private
|
|
642
|
+
#
|
|
643
|
+
# @return [Hash<String,Hash>] where the key is the port channel name,
|
|
644
|
+
# e.g. 'Port-Channel10'
|
|
645
|
+
def parse_portchannels(text) # rubocop:disable Metrics/MethodLength
|
|
646
|
+
groups = text.split('Port Channel ')
|
|
647
|
+
groups.each_with_object({}) do |str, group|
|
|
648
|
+
lines = [*str.lines]
|
|
649
|
+
name = parse_portchannel_name(lines.shift)
|
|
650
|
+
next unless name
|
|
651
|
+
active_ports = parse_portchannel_active_ports(lines)
|
|
652
|
+
configured_ports = parse_portchannel_configured_ports(lines)
|
|
653
|
+
group[name] = {
|
|
654
|
+
'name' => name,
|
|
655
|
+
'ports' => [*active_ports, *configured_ports].sort
|
|
656
|
+
}
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
private :parse_portchannels
|
|
660
|
+
|
|
661
|
+
##
|
|
662
|
+
# parse_portchannel_active_ports takes a portchannel section from `show
|
|
663
|
+
# port-channel detailed` and parses all of the active ports from the
|
|
664
|
+
# section.
|
|
665
|
+
#
|
|
666
|
+
# @param [Array<String>] lines Array of string lines for the section,
|
|
667
|
+
#
|
|
668
|
+
# @api private
|
|
669
|
+
#
|
|
670
|
+
# @return [Array<String>] Array of string port names, e.g. ['Ethernet1',
|
|
671
|
+
# 'Ethernet2']
|
|
672
|
+
def parse_portchannel_active_ports(group_lines)
|
|
673
|
+
lines = group_lines.dup
|
|
674
|
+
# Check if there are no active ports
|
|
675
|
+
mdata = /(No)? Active Ports/.match(lines.shift)
|
|
676
|
+
return [] if mdata[1] # return if there are none
|
|
677
|
+
lines.shift until /^\s*Port /.match(lines.first) || lines.empty?
|
|
678
|
+
lines.shift(2) # heading line and ---- line
|
|
679
|
+
# Read interfaces until the first blank line
|
|
680
|
+
lines.each_with_object([]) do |l, a|
|
|
681
|
+
l.chomp!
|
|
682
|
+
break a if l.empty?
|
|
683
|
+
a << l.split.first
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
|
|
687
|
+
##
|
|
688
|
+
# parse_portchannel_configured_ports takes a portchannel section from
|
|
689
|
+
# `show port-channel detailed` and parses all of the active ports from
|
|
690
|
+
# the section.
|
|
691
|
+
#
|
|
692
|
+
# @param [Array<String>] lines Array of string lines for the section,
|
|
693
|
+
#
|
|
694
|
+
# @api private
|
|
695
|
+
#
|
|
696
|
+
# @return [Array<String>] Array of string port names, e.g. ['Ethernet1',
|
|
697
|
+
# 'Ethernet2']
|
|
698
|
+
def parse_portchannel_configured_ports(group_lines)
|
|
699
|
+
lines = group_lines.dup
|
|
700
|
+
# Check if there are no active ports
|
|
701
|
+
lines.shift until /inactive ports/.match(lines.first) || lines.empty?
|
|
702
|
+
return [] if lines.empty?
|
|
703
|
+
lines.shift(3)
|
|
704
|
+
lines.each_with_object([]) do |l, a|
|
|
705
|
+
l.chomp!
|
|
706
|
+
break a if l.empty?
|
|
707
|
+
a << l.split.first
|
|
708
|
+
end
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
##
|
|
712
|
+
# parse_portchannel_name parses out the portchannel name from the first
|
|
713
|
+
# line of a group section.
|
|
714
|
+
#
|
|
715
|
+
# @param [String] line The first line of a portchannel group detailed
|
|
716
|
+
# show statement, e.g. 'Port Channel Port-Channel1 (Fallback State:
|
|
717
|
+
# Unconfigured):'
|
|
718
|
+
#
|
|
719
|
+
# @api private
|
|
720
|
+
#
|
|
721
|
+
# @return [String,nil] the name of the portchannel group or nil if the
|
|
722
|
+
# name could not be parsed.
|
|
723
|
+
def parse_portchannel_name(line)
|
|
724
|
+
mdata = /^(?:Port Channel )?(Port-Channel\d+)/.match(line)
|
|
725
|
+
mdata[1] if mdata
|
|
726
|
+
end
|
|
727
|
+
private :parse_portchannel_name
|
|
728
|
+
|
|
729
|
+
##
|
|
730
|
+
# eapi_request returns a Net::HTTP::Post instance suitable for use with
|
|
731
|
+
# the http client to make an API call to EOS. The request will
|
|
732
|
+
# automatically be initialized with an username and password if the
|
|
733
|
+
# attributes have been initialized.
|
|
734
|
+
#
|
|
735
|
+
# @param [String] request_body The data to post to the API represented as
|
|
736
|
+
# a string, usually JSON encoded.
|
|
737
|
+
#
|
|
738
|
+
# @api private
|
|
739
|
+
#
|
|
740
|
+
# @return [Net::HTTP::Post] A request instance suitable for use with
|
|
741
|
+
# Net::HTTP#request
|
|
742
|
+
def eapi_request(request_body)
|
|
743
|
+
# JSON-RPC 2.0 to /command-api/ location
|
|
744
|
+
req = Net::HTTP::Post.new('/command-api/')
|
|
745
|
+
req.basic_auth(username, password) if username && password
|
|
746
|
+
req.body = request_body
|
|
747
|
+
req
|
|
748
|
+
end
|
|
749
|
+
private :eapi_request
|
|
750
|
+
|
|
751
|
+
##
|
|
752
|
+
# eapi_call takes a string as an arista command and executes the command
|
|
753
|
+
# on the switch using the eAPI. This method decodes the API response and
|
|
754
|
+
# returns the value. For example:
|
|
755
|
+
#
|
|
756
|
+
# [1] pry(#<PuppetX::NetDev::EosApi>)> eapi_call('show version')
|
|
757
|
+
# => {"jsonrpc"=>"2.0",
|
|
758
|
+
# "result"=>
|
|
759
|
+
# [{"modelName"=>"vEOS",
|
|
760
|
+
# "internalVersion"=>"4.13.7M-1877079.4137M.1",
|
|
761
|
+
# "systemMacAddress"=>"00:42:00:08:17:78",
|
|
762
|
+
# "serialNumber"=>"",
|
|
763
|
+
# "memTotal"=>2033744,
|
|
764
|
+
# "bootupTimestamp"=>1403732020.05,
|
|
765
|
+
# "memFree"=>143688,
|
|
766
|
+
# "version"=>"4.13.7M",
|
|
767
|
+
# "architecture"=>"i386",
|
|
768
|
+
# "internalBuildId"=>"54a9c4ce-bbb0-4f6b-9448-9507de824905",
|
|
769
|
+
# "hardwareRevision"=>""}],
|
|
770
|
+
# "id"=>"a4e14732-e0f2-430d-823e-1c801273ec60"}
|
|
771
|
+
#
|
|
772
|
+
# @param [String,Array<String>] command The command or commands to
|
|
773
|
+
# execute, e.g. 'show vlan'
|
|
774
|
+
#
|
|
775
|
+
# @option opts [String] :id The identifier for this request. If omitted,
|
|
776
|
+
# a unique identifier will be generated.
|
|
777
|
+
#
|
|
778
|
+
# @option opts [String] :format ('json') The desired format of the
|
|
779
|
+
# response, e.g. 'text' or 'json'. Defaults to 'json' if not provided.
|
|
780
|
+
#
|
|
781
|
+
# @api private
|
|
782
|
+
#
|
|
783
|
+
# @return [Hash] the response from the API
|
|
784
|
+
def eapi_call(command, options = {})
|
|
785
|
+
cmds = [*command]
|
|
786
|
+
request_body = format_command(cmds, options)
|
|
787
|
+
req = eapi_request(request_body)
|
|
788
|
+
resp = http.request(req)
|
|
789
|
+
decoded_response = JSON.parse(resp.body)
|
|
790
|
+
decoded_response
|
|
791
|
+
end
|
|
792
|
+
private :eapi_call
|
|
793
|
+
|
|
794
|
+
##
|
|
795
|
+
# eapi_action makes an API call and handles any error messages in the
|
|
796
|
+
# return value.
|
|
797
|
+
#
|
|
798
|
+
# @param [String,Array<String>] command The command or commands to
|
|
799
|
+
# execute, e.g. 'show vlan'
|
|
800
|
+
#
|
|
801
|
+
# @param [String] action The action being performed, e.g. 'set interface
|
|
802
|
+
# description'. Used to format error messages on API errors.
|
|
803
|
+
#
|
|
804
|
+
# @option opts [String] :id The identifier for this request. If omitted,
|
|
805
|
+
# a unique identifier will be generated.
|
|
806
|
+
#
|
|
807
|
+
# @option opts [String] :format ('json') The desired format of the
|
|
808
|
+
# response, e.g. 'text' or 'json'. Defaults to 'json' if not provided.
|
|
809
|
+
#
|
|
810
|
+
# @api private
|
|
811
|
+
#
|
|
812
|
+
# @return [Array<Hash>] the value of the 'result' key from the API
|
|
813
|
+
# response.
|
|
814
|
+
def eapi_action(command, action = 'make api call', options = {})
|
|
815
|
+
api_response = eapi_call(command, options)
|
|
816
|
+
|
|
817
|
+
return api_response['result'] unless api_response['error']
|
|
818
|
+
err_msg = format_error(api_response['error']['data'])
|
|
819
|
+
fail ApiError, "could not #{action}: #{err_msg}"
|
|
820
|
+
end
|
|
821
|
+
private :eapi_action
|
|
822
|
+
|
|
823
|
+
##
|
|
824
|
+
# @return [URI] the URI of the server
|
|
825
|
+
def uri
|
|
826
|
+
return @uri if @uri
|
|
827
|
+
if username && password
|
|
828
|
+
@uri = URI("http://#{username}:#{password}@#{address}:#{port}")
|
|
829
|
+
else
|
|
830
|
+
@uri = URI("http://#{address}:#{port}")
|
|
831
|
+
end
|
|
832
|
+
end
|
|
833
|
+
end
|
|
834
|
+
|
|
835
|
+
##
|
|
836
|
+
# EosProviderMethods is meant to be mixed into the provider to make api
|
|
837
|
+
# methods available.
|
|
838
|
+
module EosProviderMethods
|
|
839
|
+
##
|
|
840
|
+
# api returns a memoized instance of the EosApi. This method is intended
|
|
841
|
+
# to be used from providers that have mixed in the EosProviderMethods
|
|
842
|
+
# module.
|
|
843
|
+
#
|
|
844
|
+
# @return [PuppetX::NetDev::EosApi] api instance
|
|
845
|
+
def api
|
|
846
|
+
@api ||= EosApi.new
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
##
|
|
850
|
+
# bandwidth_to_speed converts a raw bandwidth integer to a Link speed
|
|
851
|
+
# [10m|100m|1g|10g|40g|56g|100g]
|
|
852
|
+
#
|
|
853
|
+
# @param [Fixnum] bandwidth The bandwdith value in bytes per second
|
|
854
|
+
#
|
|
855
|
+
# @api public
|
|
856
|
+
#
|
|
857
|
+
# @return [String] Link speed [10m|100m|1g|10g|40g|56g|100g]
|
|
858
|
+
def bandwidth_to_speed(bandwidth)
|
|
859
|
+
if bandwidth >= 1_000_000_000
|
|
860
|
+
"#{(bandwidth / 1_000_000_000).to_i}g"
|
|
861
|
+
else
|
|
862
|
+
"#{(bandwidth / 1_000_000).to_i}m"
|
|
863
|
+
end
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
##
|
|
867
|
+
# duplex_to_value Convert a duplex string from the API response to the
|
|
868
|
+
# provider value
|
|
869
|
+
#
|
|
870
|
+
# @param [String] duplex The value from the API response
|
|
871
|
+
#
|
|
872
|
+
# @api public
|
|
873
|
+
#
|
|
874
|
+
# @return [Symbol] the value for the provider
|
|
875
|
+
def duplex_to_value(duplex)
|
|
876
|
+
case duplex
|
|
877
|
+
when 'duplexFull' then :full
|
|
878
|
+
when 'duplexHalf' then :half
|
|
879
|
+
else fail ArgumentError, "Unknown duplex value #{duplex.inspect}"
|
|
880
|
+
end
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
##
|
|
884
|
+
# interface_status_to_enable maps the interfaceStatus attribute of the
|
|
885
|
+
# API response to the enable state of :true or :false
|
|
886
|
+
#
|
|
887
|
+
# The interfaceStatus reflects realtime status so its a bit funny how it
|
|
888
|
+
# works. If interfaceStatus == 'disabled' then the interface is
|
|
889
|
+
# administratively disabled (ie configured to be disabled) otherwise its
|
|
890
|
+
# enabled (ie no shutdown). So in your conversion here you can just
|
|
891
|
+
# reflect if interfaceStatus == 'disabled' or not as the state.
|
|
892
|
+
#
|
|
893
|
+
# @param [String] status the value of interfaceStatus returned by the API
|
|
894
|
+
#
|
|
895
|
+
# @return [Symbol] :true or :false
|
|
896
|
+
def interface_status_to_enable(status)
|
|
897
|
+
status == 'disabled' ? :false : :true
|
|
898
|
+
end
|
|
899
|
+
|
|
900
|
+
##
|
|
901
|
+
# interface_attributes takes an attribute hash from the EOS API and maps
|
|
902
|
+
# the values to provider attributes for the network_interface type.
|
|
903
|
+
#
|
|
904
|
+
# @param [Hash] attr_hash Interface attribute hash
|
|
905
|
+
#
|
|
906
|
+
# @api public
|
|
907
|
+
#
|
|
908
|
+
# @return [Hash] provider attributes suitable for merge into a provider
|
|
909
|
+
# hash that will be passed to the provider initializer.
|
|
910
|
+
def interface_attributes(attr_hash)
|
|
911
|
+
hsh = {}
|
|
912
|
+
status = attr_hash['interfaceStatus']
|
|
913
|
+
hsh[:enable] = interface_status_to_enable(status)
|
|
914
|
+
hsh[:mtu] = attr_hash['mtu']
|
|
915
|
+
hsh[:speed] = bandwidth_to_speed(attr_hash['bandwidth'])
|
|
916
|
+
hsh[:duplex] = duplex_to_value(attr_hash['duplex'])
|
|
917
|
+
hsh[:description] = attr_hash['description']
|
|
918
|
+
hsh
|
|
919
|
+
end
|
|
920
|
+
|
|
921
|
+
##
|
|
922
|
+
# port_channel_attributes takes an attribute hash from the EOS API and
|
|
923
|
+
# maps the values to provider attributes for the port_channel type.
|
|
924
|
+
#
|
|
925
|
+
# @param [Hash] attr_hash Interface attribute hash
|
|
926
|
+
#
|
|
927
|
+
# @api public
|
|
928
|
+
#
|
|
929
|
+
# @return [Hash] provider attributes suitable for merge into a provider
|
|
930
|
+
# hash that will be passed to the provider initializer.
|
|
931
|
+
def port_channel_attributes(attr_hash)
|
|
932
|
+
hsh = {}
|
|
933
|
+
hsh[:speed] = bandwidth_to_speed(attr_hash['bandwidth'])
|
|
934
|
+
hsh[:description] = attr_hash['description']
|
|
935
|
+
hsh
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
##
|
|
939
|
+
# flush_speed_and_duplex consolidates the duplex and speed settings into one
|
|
940
|
+
# API call to manage the interface speed.
|
|
941
|
+
#
|
|
942
|
+
# @param [String] name The name of the interface, e.g. 'Ethernet1'
|
|
943
|
+
def flush_speed_and_duplex(name)
|
|
944
|
+
speed = convert_speed(@property_flush[:speed])
|
|
945
|
+
duplex = @property_flush[:duplex]
|
|
946
|
+
return nil unless speed || duplex
|
|
947
|
+
|
|
948
|
+
speed_out = speed ? speed : convert_speed(@property_hash[:speed])
|
|
949
|
+
duplex_out = duplex ? duplex.downcase : @property_hash[:duplex].to_s
|
|
950
|
+
|
|
951
|
+
api.set_interface_speed(name, "#{speed_out}#{duplex_out}")
|
|
952
|
+
end
|
|
953
|
+
|
|
954
|
+
##
|
|
955
|
+
# convert_speed takes a speed value from the catalog as a string and converts
|
|
956
|
+
# it to a speed prefix suitable for the Arista API. The following table is
|
|
957
|
+
# used to perform the conversion.
|
|
958
|
+
#
|
|
959
|
+
# 10000full Disable autoneg and force 10 Gbps/full duplex operation
|
|
960
|
+
# 1000full Disable autoneg and force 1 Gbps/full duplex operation
|
|
961
|
+
# 1000half Disable autoneg and force 1 Gbps/half duplex operation
|
|
962
|
+
# 100full Disable autoneg and force 100 Mbps/full duplex operation
|
|
963
|
+
# 100gfull Disable autoneg and force 100 Gbps/full duplex operation
|
|
964
|
+
# 100half Disable autoneg and force 100 Mbps/half duplex operation
|
|
965
|
+
# 10full Disable autoneg and force 10 Mbps/full duplex operation
|
|
966
|
+
# 10half Disable autoneg and force 10 Mbps/half duplex operation
|
|
967
|
+
# 40gfull Disable autoneg and force 40 Gbps/full duplex operation
|
|
968
|
+
#
|
|
969
|
+
# @param [String] speed The speed specified in the catalog, e.g. 1g
|
|
970
|
+
#
|
|
971
|
+
# @api private
|
|
972
|
+
#
|
|
973
|
+
# @return [String] The speed for the API, e.g. 1000
|
|
974
|
+
def convert_speed(value)
|
|
975
|
+
speed = value.to_s
|
|
976
|
+
if /g$/i.match(speed) && (speed.to_i > 40)
|
|
977
|
+
speed
|
|
978
|
+
elsif /g$/i.match(speed)
|
|
979
|
+
(speed.to_i * 1000).to_s
|
|
980
|
+
elsif /m$/i.match(speed)
|
|
981
|
+
speed.to_i.to_s
|
|
982
|
+
end
|
|
983
|
+
end
|
|
984
|
+
end
|
|
985
|
+
|
|
986
|
+
##
|
|
987
|
+
# EosProviderClassMethods implements common methods, e.g. `self.prefetch`
|
|
988
|
+
# for EOS providers.
|
|
989
|
+
module EosProviderClassMethods
|
|
990
|
+
##
|
|
991
|
+
# prefetch associates resources declared in the Puppet catalog with
|
|
992
|
+
# resources discovered on the system using the instances class method.
|
|
993
|
+
# Each resource that has a matching provider in the instances list will
|
|
994
|
+
# have the provider bound to the resource.
|
|
995
|
+
#
|
|
996
|
+
# @param [Hash] resources The set of resources declared in the catalog.
|
|
997
|
+
#
|
|
998
|
+
# @return [Hash<String,Puppet::Type>] catalog resources with updated
|
|
999
|
+
# provider instances.
|
|
1000
|
+
def prefetch(resources)
|
|
1001
|
+
provider_hash = instances.each_with_object({}) do |provider, hsh|
|
|
1002
|
+
hsh[provider.name] = provider
|
|
1003
|
+
end
|
|
1004
|
+
|
|
1005
|
+
resources.each_pair do |name, resource|
|
|
1006
|
+
resource.provider = provider_hash[name] if provider_hash[name]
|
|
1007
|
+
end
|
|
1008
|
+
end
|
|
1009
|
+
end
|
|
1010
|
+
end
|
|
1011
|
+
end
|