cisco_node_utils 1.0.1 → 1.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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +81 -1
- data/.travis.yml +9 -0
- data/CHANGELOG.md +72 -6
- data/CONTRIBUTING.md +32 -7
- data/README.md +70 -7
- data/Rakefile +17 -0
- data/bin/check_metric_limits.rb +109 -0
- data/bin/git/hooks/commit-msg/enforce_style +81 -0
- data/bin/git/hooks/hook_lib +108 -0
- data/bin/git/hooks/hooks-wrapper +38 -0
- data/bin/git/hooks/post-flow-hotfix-start/update-version +24 -0
- data/bin/git/hooks/post-flow-release-finish/update-version +29 -0
- data/bin/git/hooks/post-flow-release-start/update-version +19 -0
- data/bin/git/hooks/post-merge/update-hooks +6 -0
- data/bin/git/hooks/post-rewrite/update-hooks +6 -0
- data/bin/git/hooks/pre-commit/rubocop +20 -0
- data/bin/git/hooks/pre-commit/validate-diffs +31 -0
- data/bin/git/hooks/pre-push/check-changelog +24 -0
- data/bin/git/hooks/pre-push/rubocop +7 -0
- data/bin/git/update-hooks +65 -0
- data/cisco_node_utils.gemspec +9 -3
- data/docs/README-develop-best-practices.md +404 -0
- data/docs/README-develop-node-utils-APIs.md +215 -365
- data/docs/README-maintainers.md +33 -3
- data/docs/template-router.rb +89 -91
- data/docs/template-test_router.rb +52 -55
- data/lib/.rubocop.yml +18 -0
- data/lib/cisco_node_utils.rb +2 -19
- data/lib/cisco_node_utils/README_YAML.md +1 -9
- data/lib/cisco_node_utils/bgp.rb +664 -0
- data/lib/cisco_node_utils/bgp_af.rb +530 -0
- data/lib/cisco_node_utils/bgp_neighbor.rb +425 -0
- data/lib/cisco_node_utils/bgp_neighbor_af.rb +709 -0
- data/lib/cisco_node_utils/cisco_cmn_utils.rb +59 -25
- data/lib/cisco_node_utils/command_reference.rb +72 -74
- data/lib/cisco_node_utils/command_reference_common.yaml +174 -9
- data/lib/cisco_node_utils/command_reference_common_bgp.yaml +535 -0
- data/lib/cisco_node_utils/command_reference_n7k.yaml +4 -0
- data/lib/cisco_node_utils/command_reference_n9k.yaml +0 -9
- data/lib/cisco_node_utils/configparser_lib.rb +152 -147
- data/lib/cisco_node_utils/dns_domain.rb +79 -0
- data/lib/cisco_node_utils/domain_name.rb +71 -0
- data/lib/cisco_node_utils/interface.rb +167 -161
- data/lib/cisco_node_utils/interface_ospf.rb +78 -81
- data/lib/cisco_node_utils/name_server.rb +64 -0
- data/lib/cisco_node_utils/node.rb +154 -198
- data/lib/cisco_node_utils/node_util.rb +61 -0
- data/lib/cisco_node_utils/ntp_config.rb +65 -0
- data/lib/cisco_node_utils/ntp_server.rb +76 -0
- data/lib/cisco_node_utils/platform.rb +174 -165
- data/lib/cisco_node_utils/radius_global.rb +146 -0
- data/lib/cisco_node_utils/radius_server.rb +295 -0
- data/lib/cisco_node_utils/router_ospf.rb +59 -63
- data/lib/cisco_node_utils/router_ospf_vrf.rb +226 -210
- data/lib/cisco_node_utils/snmpcommunity.rb +52 -58
- data/lib/cisco_node_utils/snmpgroup.rb +22 -23
- data/lib/cisco_node_utils/snmpserver.rb +99 -103
- data/lib/cisco_node_utils/snmpuser.rb +294 -274
- data/lib/cisco_node_utils/syslog_server.rb +92 -0
- data/lib/cisco_node_utils/syslog_settings.rb +69 -0
- data/lib/cisco_node_utils/tacacs_server.rb +137 -133
- data/lib/cisco_node_utils/tacacs_server_host.rb +84 -87
- data/lib/cisco_node_utils/version.rb +2 -1
- data/lib/cisco_node_utils/vlan.rb +28 -31
- data/lib/cisco_node_utils/vrf.rb +80 -0
- data/lib/cisco_node_utils/vtp.rb +100 -97
- data/lib/cisco_node_utils/yum.rb +15 -17
- data/tests/.rubocop.yml +15 -0
- data/tests/basetest.rb +81 -36
- data/tests/ciscotest.rb +38 -78
- data/{lib/cisco_node_utils → tests}/platform_info.rb +12 -8
- data/{lib/cisco_node_utils → tests}/platform_info.yaml +1 -1
- data/tests/test_bgp_af.rb +920 -0
- data/tests/test_bgp_neighbor.rb +403 -0
- data/tests/test_bgp_neighbor_af.rb +589 -0
- data/tests/test_command_config.rb +65 -62
- data/tests/test_command_reference.rb +31 -45
- data/tests/test_dns_domain.rb +113 -0
- data/tests/test_domain_name.rb +86 -0
- data/tests/test_interface.rb +424 -548
- data/tests/test_interface_ospf.rb +248 -432
- data/tests/test_interface_svi.rb +56 -79
- data/tests/test_interface_switchport.rb +196 -272
- data/tests/test_name_server.rb +85 -0
- data/tests/test_node.rb +7 -6
- data/tests/test_node_ext.rb +133 -186
- data/tests/test_ntp_config.rb +49 -0
- data/tests/test_ntp_server.rb +74 -0
- data/tests/test_platform.rb +58 -37
- data/tests/test_radius_global.rb +78 -0
- data/tests/test_radius_server.rb +185 -0
- data/tests/test_router_bgp.rb +838 -0
- data/tests/test_router_ospf.rb +49 -80
- data/tests/test_router_ospf_vrf.rb +274 -392
- data/tests/test_snmpcommunity.rb +128 -172
- data/tests/test_snmpgroup.rb +12 -14
- data/tests/test_snmpserver.rb +160 -189
- data/tests/test_snmpuser.rb +568 -717
- data/tests/test_syslog_server.rb +88 -0
- data/tests/test_syslog_settings.rb +54 -0
- data/tests/test_tacacs_server.rb +113 -148
- data/tests/test_tacacs_server_host.rb +108 -161
- data/tests/test_vlan.rb +63 -79
- data/tests/test_vrf.rb +92 -0
- data/tests/test_vtp.rb +108 -126
- data/tests/test_yum.rb +47 -41
- metadata +92 -56
- data/.rubocop_todo.yml +0 -293
- data/docs/.rubocop.yml +0 -13
- data/docs/template-feature.rb +0 -45
- data/docs/template-test_feature.rb +0 -51
- data/tests/test_all_cisco.rb +0 -46
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
#
|
|
2
|
-
# NXAPI implementation of Interface OSPF class
|
|
3
|
-
#
|
|
4
1
|
# March 2015, Alex Hunsberger
|
|
5
2
|
#
|
|
6
3
|
# Copyright (c) 2015 Cisco and/or its affiliates.
|
|
@@ -18,58 +15,56 @@
|
|
|
18
15
|
# limitations under the License.
|
|
19
16
|
|
|
20
17
|
require 'ipaddr'
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
require_relative 'node_util'
|
|
19
|
+
require_relative 'interface'
|
|
23
20
|
# Interestingly enough, interface OSPF configuration can exist completely
|
|
24
21
|
# independent of router OSPF configuration... so we don't need RouterOspf here.
|
|
25
22
|
|
|
26
23
|
module Cisco
|
|
27
|
-
class
|
|
24
|
+
# InterfaceOspf - node utility class for per-interface OSPF config management
|
|
25
|
+
class InterfaceOspf < NodeUtil
|
|
28
26
|
attr_reader :interface, :ospf_name
|
|
29
27
|
|
|
30
|
-
@@node = Node.instance
|
|
31
|
-
|
|
32
28
|
def initialize(int_name, ospf_name, area, create=true)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
29
|
+
fail TypeError unless int_name.is_a? String
|
|
30
|
+
fail TypeError unless ospf_name.is_a? String
|
|
31
|
+
fail TypeError unless area.is_a? String
|
|
32
|
+
fail ArgumentError unless int_name.length > 0
|
|
33
|
+
fail ArgumentError unless ospf_name.length > 0
|
|
34
|
+
fail ArgumentError unless area.length > 0
|
|
39
35
|
|
|
40
36
|
# normalize
|
|
41
37
|
int_name = int_name.downcase
|
|
42
38
|
@interface = Interface.interfaces[int_name]
|
|
43
|
-
|
|
39
|
+
fail "interface #{int_name} does not exist" if @interface.nil?
|
|
44
40
|
|
|
45
41
|
@ospf_name = ospf_name
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
return unless create
|
|
44
|
+
# enable feature ospf if it isn't
|
|
45
|
+
RouterOspf.enable unless RouterOspf.enabled
|
|
50
46
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
end
|
|
47
|
+
config_set('interface_ospf', 'area', @interface.name,
|
|
48
|
+
'', @ospf_name, area)
|
|
54
49
|
end
|
|
55
50
|
|
|
56
51
|
# can't re-use Interface.interfaces because we need to filter based on
|
|
57
52
|
# "ip router ospf <name>", which Interface doesn't retrieve
|
|
58
|
-
def
|
|
59
|
-
|
|
53
|
+
def self.interfaces(ospf_name=nil)
|
|
54
|
+
fail TypeError unless ospf_name.is_a?(String) || ospf_name.nil?
|
|
60
55
|
ints = {}
|
|
61
56
|
|
|
62
|
-
intf_list =
|
|
57
|
+
intf_list = config_get('interface', 'all_interfaces')
|
|
63
58
|
return ints if intf_list.nil?
|
|
64
59
|
intf_list.each do |name|
|
|
65
|
-
match =
|
|
60
|
+
match = config_get('interface_ospf', 'area', name)
|
|
66
61
|
next if match.nil?
|
|
67
62
|
# should only be a single match under a given interface
|
|
68
63
|
match = match.first
|
|
69
64
|
# ip router ospf <name> area <area>
|
|
70
65
|
ospf = match[0]
|
|
71
66
|
area = match[1]
|
|
72
|
-
next unless ospf_name.nil?
|
|
67
|
+
next unless ospf_name.nil? || ospf == ospf_name
|
|
73
68
|
int = name.downcase
|
|
74
69
|
ints[int] = InterfaceOspf.new(int, ospf, area, false)
|
|
75
70
|
end
|
|
@@ -77,7 +72,7 @@ module Cisco
|
|
|
77
72
|
end
|
|
78
73
|
|
|
79
74
|
def area
|
|
80
|
-
match =
|
|
75
|
+
match = config_get('interface_ospf', 'area', @interface.name)
|
|
81
76
|
return nil if match.nil?
|
|
82
77
|
val = match[0][1]
|
|
83
78
|
# Coerce numeric area to the expected dot-decimal format.
|
|
@@ -86,79 +81,81 @@ module Cisco
|
|
|
86
81
|
end
|
|
87
82
|
|
|
88
83
|
def area=(a)
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
config_set('interface_ospf', 'area', @interface.name,
|
|
85
|
+
'', @ospf_name, a)
|
|
91
86
|
end
|
|
92
87
|
|
|
93
88
|
def destroy
|
|
94
|
-
|
|
95
|
-
|
|
89
|
+
config_set('interface_ospf', 'area', @interface.name,
|
|
90
|
+
'no', @ospf_name, area)
|
|
96
91
|
# Reset everything else back to default as well:
|
|
97
92
|
self.message_digest = default_message_digest
|
|
98
|
-
message_digest_key_set(default_message_digest_key_id,
|
|
93
|
+
message_digest_key_set(default_message_digest_key_id, '', '', '')
|
|
99
94
|
self.cost = default_cost
|
|
100
95
|
self.hello_interval = default_hello_interval
|
|
101
|
-
|
|
102
|
-
|
|
96
|
+
config_set('interface_ospf', 'dead_interval',
|
|
97
|
+
@interface.name, 'no', '')
|
|
103
98
|
self.passive_interface = default_passive_interface if passive_interface
|
|
104
99
|
end
|
|
105
100
|
|
|
106
101
|
def default_message_digest
|
|
107
|
-
|
|
102
|
+
config_get_default('interface_ospf', 'message_digest')
|
|
108
103
|
end
|
|
109
104
|
|
|
110
105
|
def message_digest
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
!config_get('interface_ospf', 'message_digest',
|
|
107
|
+
@interface.name).nil?
|
|
113
108
|
end
|
|
114
109
|
|
|
115
110
|
# interface %s
|
|
116
111
|
# %s ip ospf authentication message-digest
|
|
117
112
|
def message_digest=(enable)
|
|
118
|
-
|
|
119
|
-
|
|
113
|
+
config_set('interface_ospf', 'message_digest', @interface.name,
|
|
114
|
+
enable ? '' : 'no')
|
|
120
115
|
end
|
|
121
116
|
|
|
122
117
|
def default_message_digest_key_id
|
|
123
|
-
|
|
118
|
+
config_get_default('interface_ospf', 'message_digest_key_id')
|
|
124
119
|
end
|
|
125
120
|
|
|
126
121
|
def message_digest_key_id
|
|
127
|
-
match =
|
|
128
|
-
|
|
122
|
+
match = config_get('interface_ospf', 'message_digest_key_id',
|
|
123
|
+
@interface.name)
|
|
129
124
|
# regex in yaml returns an array result, use .first to get match
|
|
130
125
|
match.nil? ? default_message_digest_key_id : match.first.to_i
|
|
131
126
|
end
|
|
132
127
|
|
|
133
128
|
def default_message_digest_algorithm_type
|
|
134
|
-
|
|
135
|
-
|
|
129
|
+
config_get_default('interface_ospf',
|
|
130
|
+
'message_digest_alg_type').to_sym
|
|
136
131
|
end
|
|
137
132
|
|
|
138
133
|
def message_digest_algorithm_type
|
|
139
|
-
match =
|
|
140
|
-
|
|
134
|
+
match = config_get('interface_ospf', 'message_digest_alg_type',
|
|
135
|
+
@interface.name)
|
|
141
136
|
# regex in yaml returns an array result, use .first to get match
|
|
142
|
-
match.nil? ? default_message_digest_algorithm_type :
|
|
143
|
-
match.first.to_sym
|
|
137
|
+
match.nil? ? default_message_digest_algorithm_type : match.first.to_sym
|
|
144
138
|
end
|
|
145
139
|
|
|
146
140
|
def default_message_digest_encryption_type
|
|
147
141
|
Encryption.cli_to_symbol(
|
|
148
|
-
|
|
142
|
+
config_get_default('interface_ospf', 'message_digest_enc_type'))
|
|
149
143
|
end
|
|
150
144
|
|
|
151
145
|
def message_digest_encryption_type
|
|
152
|
-
match =
|
|
153
|
-
|
|
146
|
+
match = config_get('interface_ospf', 'message_digest_enc_type',
|
|
147
|
+
@interface.name)
|
|
154
148
|
# regex in yaml returns an array result, use .first to get match
|
|
155
|
-
match.nil?
|
|
149
|
+
if match.nil?
|
|
150
|
+
default_message_digest_encryption_type
|
|
151
|
+
else
|
|
156
152
|
Encryption.cli_to_symbol(match.first)
|
|
153
|
+
end
|
|
157
154
|
end
|
|
158
155
|
|
|
159
156
|
def message_digest_password
|
|
160
|
-
match =
|
|
161
|
-
|
|
157
|
+
match = config_get('interface_ospf', 'message_digest_password',
|
|
158
|
+
@interface.name)
|
|
162
159
|
match.nil? ? nil : match.first
|
|
163
160
|
end
|
|
164
161
|
|
|
@@ -167,89 +164,89 @@ module Cisco
|
|
|
167
164
|
def message_digest_key_set(keyid, algtype, enctype, enc)
|
|
168
165
|
current_keyid = message_digest_key_id
|
|
169
166
|
if keyid == default_message_digest_key_id && current_keyid != keyid
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
167
|
+
config_set('interface_ospf', 'message_digest_key_set',
|
|
168
|
+
@interface.name, 'no', current_keyid,
|
|
169
|
+
'', '', '')
|
|
173
170
|
elsif keyid != default_message_digest_key_id
|
|
174
|
-
|
|
175
|
-
|
|
171
|
+
fail TypeError unless enc.is_a?(String)
|
|
172
|
+
fail ArgumentError unless enc.length > 0
|
|
176
173
|
enctype = Encryption.symbol_to_cli(enctype)
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
config_set('interface_ospf', 'message_digest_key_set',
|
|
175
|
+
@interface.name, '', keyid, algtype, enctype, enc)
|
|
179
176
|
end
|
|
180
177
|
end
|
|
181
178
|
|
|
182
179
|
def cost
|
|
183
|
-
match =
|
|
180
|
+
match = config_get('interface_ospf', 'cost', @interface.name)
|
|
184
181
|
# regex in yaml returns an array result, use .first to get match
|
|
185
182
|
match.nil? ? default_cost : match.first.to_i
|
|
186
183
|
end
|
|
187
184
|
|
|
188
185
|
def default_cost
|
|
189
|
-
|
|
186
|
+
config_get_default('interface_ospf', 'cost')
|
|
190
187
|
end
|
|
191
188
|
|
|
192
189
|
# interface %s
|
|
193
190
|
# ip ospf cost %d
|
|
194
191
|
def cost=(c)
|
|
195
192
|
if c == default_cost
|
|
196
|
-
|
|
193
|
+
config_set('interface_ospf', 'cost', @interface.name, 'no', '')
|
|
197
194
|
else
|
|
198
|
-
|
|
195
|
+
config_set('interface_ospf', 'cost', @interface.name, '', c)
|
|
199
196
|
end
|
|
200
197
|
end
|
|
201
198
|
|
|
202
199
|
def hello_interval
|
|
203
|
-
match =
|
|
204
|
-
|
|
200
|
+
match = config_get('interface_ospf', 'hello_interval',
|
|
201
|
+
@interface.name)
|
|
205
202
|
# regex in yaml returns an array result, use .first to get match
|
|
206
203
|
match.nil? ? default_hello_interval : match.first.to_i
|
|
207
204
|
end
|
|
208
205
|
|
|
209
206
|
def default_hello_interval
|
|
210
|
-
|
|
207
|
+
config_get_default('interface_ospf', 'hello_interval')
|
|
211
208
|
end
|
|
212
209
|
|
|
213
210
|
# interface %s
|
|
214
211
|
# ip ospf hello-interval %d
|
|
215
212
|
def hello_interval=(interval)
|
|
216
|
-
|
|
217
|
-
|
|
213
|
+
config_set('interface_ospf', 'hello_interval',
|
|
214
|
+
@interface.name, '', interval.to_i)
|
|
218
215
|
end
|
|
219
216
|
|
|
220
217
|
def dead_interval
|
|
221
|
-
match =
|
|
222
|
-
|
|
218
|
+
match = config_get('interface_ospf', 'dead_interval',
|
|
219
|
+
@interface.name)
|
|
223
220
|
# regex in yaml returns an array result, use .first to get match
|
|
224
221
|
match.nil? ? default_dead_interval : match.first.to_i
|
|
225
222
|
end
|
|
226
223
|
|
|
227
224
|
def default_dead_interval
|
|
228
|
-
|
|
225
|
+
config_get_default('interface_ospf', 'dead_interval')
|
|
229
226
|
end
|
|
230
227
|
|
|
231
228
|
# interface %s
|
|
232
229
|
# ip ospf dead-interval %d
|
|
233
230
|
def dead_interval=(interval)
|
|
234
|
-
|
|
235
|
-
|
|
231
|
+
config_set('interface_ospf', 'dead_interval',
|
|
232
|
+
@interface.name, '', interval.to_i)
|
|
236
233
|
end
|
|
237
234
|
|
|
238
235
|
def default_passive_interface
|
|
239
|
-
|
|
236
|
+
config_get_default('interface_ospf', 'passive_interface')
|
|
240
237
|
end
|
|
241
238
|
|
|
242
239
|
def passive_interface
|
|
243
|
-
|
|
244
|
-
|
|
240
|
+
!config_get('interface_ospf', 'passive_interface',
|
|
241
|
+
@interface.name).nil?
|
|
245
242
|
end
|
|
246
243
|
|
|
247
244
|
# interface %s
|
|
248
245
|
# %s ip ospf passive-interface
|
|
249
246
|
def passive_interface=(enable)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
247
|
+
fail TypeError unless enable == true || enable == false
|
|
248
|
+
config_set('interface_ospf', 'passive_interface', @interface.name,
|
|
249
|
+
enable ? '' : 'no')
|
|
253
250
|
end
|
|
254
251
|
end
|
|
255
252
|
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#
|
|
2
|
+
# NXAPI implementation of NameServer class
|
|
3
|
+
#
|
|
4
|
+
# September 2015, Hunter Haugen
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2015 Cisco and/or its affiliates.
|
|
7
|
+
#
|
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
+
# you may not use this file except in compliance with the License.
|
|
10
|
+
# You may obtain a copy of the License at
|
|
11
|
+
#
|
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
+
#
|
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
+
# See the License for the specific language governing permissions and
|
|
18
|
+
# limitations under the License.
|
|
19
|
+
#
|
|
20
|
+
# "group" is a standard SNMP term but in NXOS "role" is used to serve the
|
|
21
|
+
# purpose of group; thus this provider utility does not create snmp groups
|
|
22
|
+
# and is limited to reporting group (role) existence only.
|
|
23
|
+
|
|
24
|
+
require_relative 'node_util'
|
|
25
|
+
|
|
26
|
+
module Cisco
|
|
27
|
+
# NameServer - node utility class for DNS client name server config management
|
|
28
|
+
class NameServer < NodeUtil
|
|
29
|
+
attr_reader :name
|
|
30
|
+
|
|
31
|
+
def initialize(name, instantiate=true)
|
|
32
|
+
unless name.is_a? String
|
|
33
|
+
fail TypeError, "Expected a string, got a #{name.inspect}"
|
|
34
|
+
end
|
|
35
|
+
@name = name
|
|
36
|
+
create if instantiate
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.nameservers
|
|
40
|
+
hosts = config_get('dnsclient', 'name_server')
|
|
41
|
+
return {} if hosts.nil?
|
|
42
|
+
|
|
43
|
+
hash = {}
|
|
44
|
+
# Join and split because config_get returns array of strings separated by
|
|
45
|
+
# spaces (regexes are a subset of PDA)
|
|
46
|
+
hosts.join(' ').split(' ').each do |name|
|
|
47
|
+
hash[name] = NameServer.new(name, false)
|
|
48
|
+
end
|
|
49
|
+
hash
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def ==(other)
|
|
53
|
+
name == other.name
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def create
|
|
57
|
+
config_set('dnsclient', 'name_server', state: '', ip: @name)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def destroy
|
|
61
|
+
config_set('dnsclient', 'name_server', state: 'no', ip: @name)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -21,8 +21,9 @@
|
|
|
21
21
|
require 'singleton'
|
|
22
22
|
|
|
23
23
|
require 'cisco_nxapi'
|
|
24
|
-
|
|
24
|
+
require_relative 'command_reference'
|
|
25
25
|
|
|
26
|
+
# Add node management classes and APIs to the Cisco namespace.
|
|
26
27
|
module Cisco
|
|
27
28
|
# Error class raised by the config_set and config_get APIs if the
|
|
28
29
|
# device encounters an issue trying to act on the requested CLI.
|
|
@@ -33,7 +34,7 @@ module Cisco
|
|
|
33
34
|
attr_reader :command, :clierror, :previous
|
|
34
35
|
def initialize(command, clierror, previous)
|
|
35
36
|
@command = command
|
|
36
|
-
@clierror = clierror.rstrip
|
|
37
|
+
@clierror = clierror.rstrip if clierror.kind_of? String
|
|
37
38
|
@previous = previous
|
|
38
39
|
end
|
|
39
40
|
|
|
@@ -46,7 +47,6 @@ module Cisco
|
|
|
46
47
|
# Singleton representing the network node (switch/router) that is
|
|
47
48
|
# running this code. The singleton is lazily instantiated, meaning that
|
|
48
49
|
# it doesn't exist until some client requests it (with Node.instance())
|
|
49
|
-
|
|
50
50
|
class Node
|
|
51
51
|
include Singleton
|
|
52
52
|
|
|
@@ -61,14 +61,54 @@ module Cisco
|
|
|
61
61
|
#
|
|
62
62
|
# @raise [IndexError] if the given (feature, name) pair is not in the
|
|
63
63
|
# CommandReference data or if the data doesn't have values defined
|
|
64
|
-
# for the 'config_get' and 'config_get_token' fields.
|
|
64
|
+
# for the 'config_get' and (optional) 'config_get_token' fields.
|
|
65
65
|
# @raise [Cisco::CliError] if the given command is rejected by the device.
|
|
66
66
|
#
|
|
67
67
|
# @param feature [String]
|
|
68
68
|
# @param name [String]
|
|
69
|
-
# @return [String]
|
|
69
|
+
# @return [String, Hash, Array]
|
|
70
70
|
# @example config_get("show_version", "system_image")
|
|
71
|
-
|
|
71
|
+
# @example config_get("ospf", "router_id",
|
|
72
|
+
# {:name => "green", :vrf => "one"})
|
|
73
|
+
def config_get(feature, name, *args)
|
|
74
|
+
fail 'lazy_connect specified but did not request connect' unless @cmd_ref
|
|
75
|
+
ref = @cmd_ref.lookup(feature, name)
|
|
76
|
+
|
|
77
|
+
begin
|
|
78
|
+
token = build_config_get_token(feature, ref, args)
|
|
79
|
+
rescue IndexError, TypeError
|
|
80
|
+
# IndexError if value is not set, TypeError if set to nil explicitly
|
|
81
|
+
token = nil
|
|
82
|
+
end
|
|
83
|
+
if token.kind_of?(String)
|
|
84
|
+
if token[0] == '/' && token[-1] == '/'
|
|
85
|
+
fail RuntimeError unless args.length == token.scan(/%/).length
|
|
86
|
+
# convert string to regexp and replace %s with args
|
|
87
|
+
token = Regexp.new(sprintf(token, *args)[1..-2])
|
|
88
|
+
text = build_config_get(feature, ref, :ascii)
|
|
89
|
+
return Cisco.find_ascii(text, token)
|
|
90
|
+
else
|
|
91
|
+
hash = build_config_get(feature, ref, :structured)
|
|
92
|
+
return hash[token]
|
|
93
|
+
end
|
|
94
|
+
elsif token.kind_of?(Array)
|
|
95
|
+
# Array of /regexps/ -> ascii, array of strings/ints -> structured
|
|
96
|
+
if token[0].kind_of?(String) &&
|
|
97
|
+
token[0][0] == '/' &&
|
|
98
|
+
(token[0][-1] == '/' || token[0][-2..-1] == '/i')
|
|
99
|
+
|
|
100
|
+
token = token_str_to_regexp(token, args)
|
|
101
|
+
text = build_config_get(feature, ref, :ascii)
|
|
102
|
+
return Cisco.find_ascii(text, token[-1], *token[0..-2])
|
|
103
|
+
|
|
104
|
+
else
|
|
105
|
+
result = build_config_get(feature, ref, :structured)
|
|
106
|
+
return config_get_handle_structured(token, result)
|
|
107
|
+
end
|
|
108
|
+
elsif token.nil?
|
|
109
|
+
return show(ref.config_get, :structured)
|
|
110
|
+
end
|
|
111
|
+
fail TypeError("Unclear to handle config_get_token #{token}")
|
|
72
112
|
end
|
|
73
113
|
|
|
74
114
|
# Uses CommandReference to lookup the default value for a given
|
|
@@ -82,6 +122,9 @@ module Cisco
|
|
|
82
122
|
# @return [String]
|
|
83
123
|
# @example config_get_default("vtp", "file")
|
|
84
124
|
def config_get_default(feature, name)
|
|
125
|
+
fail 'lazy_connect specified but did not request connect' unless @cmd_ref
|
|
126
|
+
ref = @cmd_ref.lookup(feature, name)
|
|
127
|
+
ref.default_value
|
|
85
128
|
end
|
|
86
129
|
|
|
87
130
|
# Uses CommandReference to look up the given config command(s) of interest
|
|
@@ -95,7 +138,37 @@ module Cisco
|
|
|
95
138
|
# @param name [String]
|
|
96
139
|
# @param args [*String] zero or more args to be substituted into the cmdref.
|
|
97
140
|
# @example config_set("vtp", "domain", "example.com")
|
|
141
|
+
# @example config_set("ospf", "router_id",
|
|
142
|
+
# {:name => "green", :vrf => "one", :state => "",
|
|
143
|
+
# :router_id => "192.0.0.1"})
|
|
98
144
|
def config_set(feature, name, *args)
|
|
145
|
+
fail 'lazy_connect specified but did not request connect' unless @cmd_ref
|
|
146
|
+
ref = @cmd_ref.lookup(feature, name)
|
|
147
|
+
config_set = build_config_set(feature, ref, args)
|
|
148
|
+
if config_set.is_a?(String)
|
|
149
|
+
param_count = config_set.scan(/%/).length
|
|
150
|
+
elsif config_set.is_a?(Array)
|
|
151
|
+
param_count = config_set.join(' ').scan(/%/).length
|
|
152
|
+
else
|
|
153
|
+
fail TypeError, '%{config_set.class} not supported for config_set'
|
|
154
|
+
end
|
|
155
|
+
unless args[0].is_a? Hash
|
|
156
|
+
if param_count != args.length
|
|
157
|
+
fail ArgumentError, 'Wrong number of params - expected: ' \
|
|
158
|
+
"#{param_count} actual: #{args.length}"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
if config_set.is_a?(String)
|
|
162
|
+
config(sprintf(config_set, *args))
|
|
163
|
+
elsif config_set.is_a?(Array)
|
|
164
|
+
new_config_set = []
|
|
165
|
+
config_set.each do |line|
|
|
166
|
+
param_count = line.scan(/%/).length
|
|
167
|
+
new_config_set << sprintf(line, *args.first(param_count))
|
|
168
|
+
args = args[param_count..-1]
|
|
169
|
+
end
|
|
170
|
+
config(new_config_set)
|
|
171
|
+
end
|
|
99
172
|
end
|
|
100
173
|
|
|
101
174
|
# Clear the cache of CLI output results.
|
|
@@ -104,25 +177,29 @@ module Cisco
|
|
|
104
177
|
# whenever a config_set() is called, but providers may also call this
|
|
105
178
|
# to explicitly force the cache to be cleared.
|
|
106
179
|
def cache_flush
|
|
180
|
+
@client.cache_flush
|
|
107
181
|
end
|
|
108
182
|
|
|
109
|
-
# END NODE API
|
|
110
183
|
# Here and below are implementation details and private APIs that most
|
|
111
184
|
# providers shouldn't need to know about or use.
|
|
112
185
|
|
|
113
186
|
attr_reader :cmd_ref, :client
|
|
114
187
|
|
|
115
188
|
# For unit testing - we won't know the node connection info at load time.
|
|
116
|
-
|
|
189
|
+
@lazy_connect = false
|
|
190
|
+
|
|
191
|
+
class << self
|
|
192
|
+
attr_reader :lazy_connect
|
|
193
|
+
end
|
|
117
194
|
|
|
118
|
-
|
|
119
|
-
|
|
195
|
+
class << self
|
|
196
|
+
attr_writer :lazy_connect
|
|
120
197
|
end
|
|
121
198
|
|
|
122
199
|
def initialize
|
|
123
200
|
@client = nil
|
|
124
201
|
@cmd_ref = nil
|
|
125
|
-
connect unless
|
|
202
|
+
connect unless self.class.lazy_connect
|
|
126
203
|
end
|
|
127
204
|
|
|
128
205
|
def to_s
|
|
@@ -141,13 +218,6 @@ module Cisco
|
|
|
141
218
|
@client.reload
|
|
142
219
|
end
|
|
143
220
|
|
|
144
|
-
# hidden as well
|
|
145
|
-
attr_reader :client
|
|
146
|
-
|
|
147
|
-
def cache_flush
|
|
148
|
-
@client.cache_flush
|
|
149
|
-
end
|
|
150
|
-
|
|
151
221
|
def cache_enable?
|
|
152
222
|
@client.cache_enable?
|
|
153
223
|
end
|
|
@@ -175,19 +245,19 @@ module Cisco
|
|
|
175
245
|
def token_str_to_regexp(token, args)
|
|
176
246
|
unless args[0].is_a? Hash
|
|
177
247
|
expected_args = token.join.scan(/%/).length
|
|
178
|
-
|
|
248
|
+
fail "Given #{args.length} args, but token #{token} requires " \
|
|
179
249
|
"#{expected_args}" unless args.length == expected_args
|
|
180
250
|
end
|
|
181
251
|
# replace all %s with *args
|
|
182
252
|
token.map! { |str| sprintf(str, *args.shift(str.scan(/%/).length)) }
|
|
183
253
|
# convert all to Regexp objects
|
|
184
|
-
token.map!
|
|
254
|
+
token.map! do |str|
|
|
185
255
|
if str[-2..-1] == '/i'
|
|
186
256
|
Regexp.new(str[1..-3], Regexp::IGNORECASE)
|
|
187
257
|
else
|
|
188
258
|
Regexp.new(str[1..-2])
|
|
189
259
|
end
|
|
190
|
-
|
|
260
|
+
end
|
|
191
261
|
token
|
|
192
262
|
end
|
|
193
263
|
|
|
@@ -210,7 +280,7 @@ module Cisco
|
|
|
210
280
|
replace = regexp.scan(/<(\S+)>/).flatten.map(&:to_sym)
|
|
211
281
|
replace.each do |item|
|
|
212
282
|
regexp = regexp.sub "<#{item}>",
|
|
213
|
-
|
|
283
|
+
values[item].to_s if values.key?(item)
|
|
214
284
|
end
|
|
215
285
|
# Only return lines that actually replaced ids or did not have any
|
|
216
286
|
# ids to replace. Implicit nil returned if not.
|
|
@@ -235,7 +305,7 @@ module Cisco
|
|
|
235
305
|
# @param ref [CommandReference::CmdRef]
|
|
236
306
|
# @return [String, Array]
|
|
237
307
|
def build_config_get_token(feature, ref, args)
|
|
238
|
-
|
|
308
|
+
fail 'lazy_connect specified but did not request connect' unless @cmd_ref
|
|
239
309
|
# Why clone token? A bug in some ruby versions caused token to convert
|
|
240
310
|
# to type Regexp unexpectedly. The clone hard copy resolved it.
|
|
241
311
|
|
|
@@ -247,7 +317,7 @@ module Cisco
|
|
|
247
317
|
# Use _template yaml entry if config_get_token_append
|
|
248
318
|
if ref.to_s[/config_get_token_append/]
|
|
249
319
|
# Get yaml feature template:
|
|
250
|
-
template = @cmd_ref.lookup(feature,
|
|
320
|
+
template = @cmd_ref.lookup(feature, '_template')
|
|
251
321
|
# Process config_get_token: from template:
|
|
252
322
|
token.push(replace_token_ids(template.config_get_token, options))
|
|
253
323
|
# Process config_get_token_append sequence: from template:
|
|
@@ -272,13 +342,13 @@ module Cisco
|
|
|
272
342
|
# @param type [Symbol]
|
|
273
343
|
# @return [String, Array]
|
|
274
344
|
def build_config_get(feature, ref, type)
|
|
275
|
-
|
|
345
|
+
fail 'lazy_connect specified but did not request connect' unless @cmd_ref
|
|
276
346
|
# Use feature name config_get string if present
|
|
277
347
|
# else use feature template: config_get
|
|
278
|
-
if ref.hash.key?(
|
|
348
|
+
if ref.hash.key?('config_get')
|
|
279
349
|
return show(ref.config_get, type)
|
|
280
350
|
else
|
|
281
|
-
template = @cmd_ref.lookup(feature,
|
|
351
|
+
template = @cmd_ref.lookup(feature, '_template')
|
|
282
352
|
return show(template.config_get, type)
|
|
283
353
|
end
|
|
284
354
|
end
|
|
@@ -291,7 +361,7 @@ module Cisco
|
|
|
291
361
|
# @param ref [CommandReference::CmdRef]
|
|
292
362
|
# @return [String, Array]
|
|
293
363
|
def build_config_set(feature, ref, args)
|
|
294
|
-
|
|
364
|
+
fail 'lazy_connect specified but did not request connect' unless @cmd_ref
|
|
295
365
|
# If the options are presented as type Hash process as
|
|
296
366
|
# key-value replacement pairs
|
|
297
367
|
return ref.config_set unless args[0].is_a?(Hash)
|
|
@@ -300,7 +370,7 @@ module Cisco
|
|
|
300
370
|
# Use _template yaml entry if config_set_append
|
|
301
371
|
if ref.to_s[/config_set_append/]
|
|
302
372
|
# Get yaml feature template:
|
|
303
|
-
template = @cmd_ref.lookup(feature,
|
|
373
|
+
template = @cmd_ref.lookup(feature, '_template')
|
|
304
374
|
# Process config_set: from template:
|
|
305
375
|
config_set.push(replace_token_ids(template.config_set, options))
|
|
306
376
|
# Process config_set_append sequence: from template:
|
|
@@ -317,146 +387,31 @@ module Cisco
|
|
|
317
387
|
config_set
|
|
318
388
|
end
|
|
319
389
|
|
|
320
|
-
#
|
|
321
|
-
#
|
|
322
|
-
#
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
ref = @cmd_ref.lookup(feature, name)
|
|
339
|
-
|
|
340
|
-
begin
|
|
341
|
-
token = build_config_get_token(feature, ref, args)
|
|
342
|
-
rescue IndexError, TypeError
|
|
343
|
-
# IndexError if value is not set, TypeError if set to nil explicitly
|
|
344
|
-
token = nil
|
|
345
|
-
end
|
|
346
|
-
if token.kind_of?(String) and token[0] == '/' and token[-1] == '/'
|
|
347
|
-
raise RuntimeError unless args.length == token.scan(/%/).length
|
|
348
|
-
# convert string to regexp and replace %s with args
|
|
349
|
-
token = Regexp.new(sprintf(token, *args)[1..-2])
|
|
350
|
-
text = build_config_get(feature, ref, :ascii)
|
|
351
|
-
return Cisco.find_ascii(text, token)
|
|
352
|
-
elsif token.kind_of?(String)
|
|
353
|
-
hash = build_config_get(feature, ref, :structured)
|
|
354
|
-
return hash[token]
|
|
355
|
-
|
|
356
|
-
elsif token.kind_of?(Array)
|
|
357
|
-
# Array of /regexps/ -> ascii, array of strings/ints -> structured
|
|
358
|
-
if token[0].kind_of?(String) and
|
|
359
|
-
token[0][0] == '/' and
|
|
360
|
-
(token[0][-1] == '/' or token[0][-2..-1] == '/i')
|
|
361
|
-
|
|
362
|
-
token = token_str_to_regexp(token, args)
|
|
363
|
-
text = build_config_get(feature, ref, :ascii)
|
|
364
|
-
return Cisco.find_ascii(text, token[-1], *token[0..-2])
|
|
365
|
-
|
|
366
|
-
else
|
|
367
|
-
result = build_config_get(feature, ref, :structured)
|
|
368
|
-
begin
|
|
369
|
-
token.each do |token|
|
|
370
|
-
# if token is a hash and result is an array, check each
|
|
371
|
-
# array index (which should return another hash) to see if
|
|
372
|
-
# it contains the matching key/value pairs specified in token,
|
|
373
|
-
# and return the first match (or nil)
|
|
374
|
-
if token.kind_of?(Hash)
|
|
375
|
-
raise "Expected array, got #{result.class}" unless result.kind_of?(Array)
|
|
376
|
-
result = result.select { |x| token.all? { |k, v| x[k] == v } }
|
|
377
|
-
raise "Multiple matches found for #{token}" if result.length > 1
|
|
378
|
-
raise "No match found for #{token}" if result.length == 0
|
|
379
|
-
result = result[0]
|
|
380
|
-
else # result is array or hash
|
|
381
|
-
raise "No key \"#{token}\" in #{result}" if result[token].nil?
|
|
382
|
-
result = result[token]
|
|
383
|
-
end
|
|
384
|
-
end
|
|
385
|
-
return result
|
|
386
|
-
rescue Exception => e
|
|
387
|
-
# TODO: logging user story, Syslog isn't available here
|
|
388
|
-
# Syslog.debug(e.message)
|
|
389
|
-
return nil
|
|
390
|
-
end
|
|
391
|
-
end
|
|
392
|
-
elsif token.nil?
|
|
393
|
-
return show(ref.config_get, :structured)
|
|
394
|
-
end
|
|
395
|
-
raise TypeError("Unclear to handle config_get_token #{token}")
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
# Uses CommandReference to lookup the default value for a given
|
|
399
|
-
# feature and feature property.
|
|
400
|
-
#
|
|
401
|
-
# @raise [IndexError] if the given (feature, name) pair is not in the
|
|
402
|
-
# CommandReference data or if the data doesn't have values defined
|
|
403
|
-
# for the 'default_value' field.
|
|
404
|
-
# @param feature [String]
|
|
405
|
-
# @param name [String]
|
|
406
|
-
# @return [String]
|
|
407
|
-
# @example config_get_default("vtp", "file")
|
|
408
|
-
def config_get_default(feature, name)
|
|
409
|
-
raise "lazy_connect specified but did not request connect" unless @cmd_ref
|
|
410
|
-
ref = @cmd_ref.lookup(feature, name)
|
|
411
|
-
ref.default_value
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
# Uses CommandReference to look up the given config command(s) of interest
|
|
415
|
-
# and then applies the configuration.
|
|
416
|
-
#
|
|
417
|
-
# @raise [IndexError] if no relevant cmd_ref config_set exists
|
|
418
|
-
# @raise [ArgumentError] if too many or too few args are provided.
|
|
419
|
-
# @raise [Cisco::CliError] if any command is rejected by the device.
|
|
420
|
-
#
|
|
421
|
-
# @param feature [String]
|
|
422
|
-
# @param name [String]
|
|
423
|
-
# @param args [*String] zero or more args to be substituted into the cmdref.
|
|
424
|
-
# @example config_set("vtp", "domain", "example.com")
|
|
425
|
-
# @example config_set("ospf", "router_id",
|
|
426
|
-
# {:name => "green", :vrf => "one", :state => "",
|
|
427
|
-
# :router_id => "192.0.0.1"})
|
|
428
|
-
def config_set(feature, name, *args)
|
|
429
|
-
raise "lazy_connect specified but did not request connect" unless @cmd_ref
|
|
430
|
-
ref = @cmd_ref.lookup(feature, name)
|
|
431
|
-
config_set = build_config_set(feature, ref, args)
|
|
432
|
-
if config_set.is_a?(String)
|
|
433
|
-
param_count = config_set.scan(/%/).length
|
|
434
|
-
elsif config_set.is_a?(Array)
|
|
435
|
-
param_count = config_set.join(" ").scan(/%/).length
|
|
436
|
-
else
|
|
437
|
-
raise TypeError, "%{config_set.class} not supported for config_set"
|
|
438
|
-
end
|
|
439
|
-
unless args[0].is_a? Hash
|
|
440
|
-
if param_count != args.length
|
|
441
|
-
raise ArgumentError.new("Wrong number of params - expected: " +
|
|
442
|
-
"#{param_count} actual: #{args.length}")
|
|
390
|
+
# Helper method for config_get().
|
|
391
|
+
# @param token [Array, Hash] lookup sequence
|
|
392
|
+
# @param result [Array, Hash] structured output from node
|
|
393
|
+
def config_get_handle_structured(token, result)
|
|
394
|
+
token.each do |t|
|
|
395
|
+
# if token is a hash and result is an array, check each
|
|
396
|
+
# array index (which should return another hash) to see if
|
|
397
|
+
# it contains the matching key/value pairs specified in token,
|
|
398
|
+
# and return the first match (or nil)
|
|
399
|
+
if t.kind_of?(Hash)
|
|
400
|
+
fail "Expected array, got #{result.class}" unless result.is_a? Array
|
|
401
|
+
result = result.select { |x| t.all? { |k, v| x[k] == v } }
|
|
402
|
+
fail "Multiple matches found for #{t}" if result.length > 1
|
|
403
|
+
fail "No match found for #{t}" if result.length == 0
|
|
404
|
+
result = result[0]
|
|
405
|
+
else # result is array or hash
|
|
406
|
+
fail "No key \"#{t}\" in #{result}" if result[t].nil?
|
|
407
|
+
result = result[t]
|
|
443
408
|
end
|
|
444
409
|
end
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
param_count = line.scan(/%/).length
|
|
451
|
-
if param_count > 0
|
|
452
|
-
new_config_set << sprintf(line, *args)
|
|
453
|
-
args = args[param_count..-1]
|
|
454
|
-
else
|
|
455
|
-
new_config_set << line
|
|
456
|
-
end
|
|
457
|
-
end
|
|
458
|
-
config(new_config_set)
|
|
459
|
-
end
|
|
410
|
+
result
|
|
411
|
+
rescue RuntimeError
|
|
412
|
+
# TODO: logging user story, Syslog isn't available here
|
|
413
|
+
# Syslog.debug(e.message)
|
|
414
|
+
nil
|
|
460
415
|
end
|
|
461
416
|
|
|
462
417
|
# Send a config command to the device.
|
|
@@ -465,6 +420,7 @@ module Cisco
|
|
|
465
420
|
#
|
|
466
421
|
# @raise [Cisco::CliError] if any command is rejected by the device.
|
|
467
422
|
def config(commands)
|
|
423
|
+
CiscoLogger.debug("CLI Sent to device: #{commands}")
|
|
468
424
|
@client.config(commands)
|
|
469
425
|
rescue CiscoNxapi::CliError => e
|
|
470
426
|
raise Cisco::CliError.new(e.input, e.clierror, e.previous)
|
|
@@ -483,52 +439,52 @@ module Cisco
|
|
|
483
439
|
|
|
484
440
|
# @return [String] such as "Cisco Nexus Operating System (NX-OS) Software"
|
|
485
441
|
def os
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
442
|
+
o = config_get('show_version', 'header')
|
|
443
|
+
fail 'failed to retrieve operating system information' if o.nil?
|
|
444
|
+
o.split("\n")[0]
|
|
489
445
|
end
|
|
490
446
|
|
|
491
447
|
# @return [String] such as "6.0(2)U5(1) [build 6.0(2)U5(0.941)]"
|
|
492
448
|
def os_version
|
|
493
|
-
config_get(
|
|
449
|
+
config_get('show_version', 'version')
|
|
494
450
|
end
|
|
495
451
|
|
|
496
452
|
# @return [String] such as "Nexus 3048 Chassis"
|
|
497
453
|
def product_description
|
|
498
|
-
config_get(
|
|
454
|
+
config_get('show_version', 'description')
|
|
499
455
|
end
|
|
500
456
|
|
|
501
457
|
# @return [String] such as "N3K-C3048TP-1GE"
|
|
502
458
|
def product_id
|
|
503
459
|
if @cmd_ref
|
|
504
|
-
return config_get(
|
|
460
|
+
return config_get('inventory', 'productid')
|
|
505
461
|
else
|
|
506
462
|
# We use this function to *find* the appropriate CommandReference
|
|
507
|
-
entries = show(
|
|
508
|
-
return entries[
|
|
463
|
+
entries = show('show inventory', :structured)
|
|
464
|
+
return entries['TABLE_inv']['ROW_inv'][0]['productid']
|
|
509
465
|
end
|
|
510
466
|
end
|
|
511
467
|
|
|
512
468
|
# @return [String] such as "V01"
|
|
513
469
|
def product_version_id
|
|
514
|
-
config_get(
|
|
470
|
+
config_get('inventory', 'versionid')
|
|
515
471
|
end
|
|
516
472
|
|
|
517
473
|
# @return [String] such as "FOC1722R0ET"
|
|
518
474
|
def product_serial_number
|
|
519
|
-
config_get(
|
|
475
|
+
config_get('inventory', 'serialnum')
|
|
520
476
|
end
|
|
521
477
|
|
|
522
478
|
# @return [String] such as "bxb-oa-n3k-7"
|
|
523
479
|
def host_name
|
|
524
|
-
config_get(
|
|
480
|
+
config_get('show_version', 'host_name')
|
|
525
481
|
end
|
|
526
482
|
|
|
527
483
|
# @return [String] such as "example.com"
|
|
528
484
|
def domain_name
|
|
529
|
-
result = config_get(
|
|
485
|
+
result = config_get('dnsclient', 'domain_name')
|
|
530
486
|
if result.nil?
|
|
531
|
-
return
|
|
487
|
+
return ''
|
|
532
488
|
else
|
|
533
489
|
return result[0]
|
|
534
490
|
end
|
|
@@ -537,8 +493,8 @@ module Cisco
|
|
|
537
493
|
# @return [Integer] System uptime, in seconds
|
|
538
494
|
def system_uptime
|
|
539
495
|
cache_flush
|
|
540
|
-
t = config_get(
|
|
541
|
-
|
|
496
|
+
t = config_get('show_system', 'uptime')
|
|
497
|
+
fail 'failed to retrieve system uptime' if t.nil?
|
|
542
498
|
t = t.shift
|
|
543
499
|
# time units: t = ["0", "23", "15", "49"]
|
|
544
500
|
t.map!(&:to_i)
|
|
@@ -548,8 +504,8 @@ module Cisco
|
|
|
548
504
|
|
|
549
505
|
# @return [String] timestamp of last reset time
|
|
550
506
|
def last_reset_time
|
|
551
|
-
output = config_get(
|
|
552
|
-
return
|
|
507
|
+
output = config_get('show_version', 'last_reset_time')
|
|
508
|
+
return '' if output.nil?
|
|
553
509
|
# NX-OS may provide leading/trailing whitespace:
|
|
554
510
|
# " Sat Oct 25 00:39:25 2014\n"
|
|
555
511
|
# so be sure to strip() it down to the actual string.
|
|
@@ -558,26 +514,26 @@ module Cisco
|
|
|
558
514
|
|
|
559
515
|
# @return [String] such as "Reset Requested by CLI command reload"
|
|
560
516
|
def last_reset_reason
|
|
561
|
-
config_get(
|
|
517
|
+
config_get('show_version', 'last_reset_reason')
|
|
562
518
|
end
|
|
563
519
|
|
|
564
520
|
# @return [Float] combined user/kernel CPU utilization
|
|
565
521
|
def system_cpu_utilization
|
|
566
|
-
output = config_get(
|
|
567
|
-
|
|
568
|
-
output[
|
|
522
|
+
output = config_get('system', 'resources')
|
|
523
|
+
fail 'failed to retrieve cpu utilization' if output.nil?
|
|
524
|
+
output['cpu_state_user'].to_f + output['cpu_state_kernel'].to_f
|
|
569
525
|
end
|
|
570
526
|
|
|
571
527
|
# @return [String] such as
|
|
572
528
|
# "bootflash:///n3000-uk9-kickstart.6.0.2.U5.0.941.bin"
|
|
573
529
|
def boot
|
|
574
|
-
config_get(
|
|
530
|
+
config_get('show_version', 'boot_image')
|
|
575
531
|
end
|
|
576
532
|
|
|
577
533
|
# @return [String] such as
|
|
578
534
|
# "bootflash:///n3000-uk9.6.0.2.U5.0.941.bin"
|
|
579
535
|
def system
|
|
580
|
-
config_get(
|
|
536
|
+
config_get('show_version', 'system_image')
|
|
581
537
|
end
|
|
582
538
|
end
|
|
583
539
|
|
|
@@ -598,8 +554,8 @@ module Cisco
|
|
|
598
554
|
# => 'example.com'
|
|
599
555
|
def find_one_ascii(body, regex_query, *parent_cfg)
|
|
600
556
|
matches = find_ascii(body, regex_query, *parent_cfg)
|
|
601
|
-
return
|
|
602
|
-
|
|
557
|
+
return '' if matches.nil?
|
|
558
|
+
fail RuntimeError if matches.length > 1
|
|
603
559
|
matches[0]
|
|
604
560
|
end
|
|
605
561
|
module_function :find_one_ascii
|
|
@@ -623,11 +579,11 @@ module Cisco
|
|
|
623
579
|
# bgp_afs = find_ascii(show_run_bgp, /^address-family (.*)/,
|
|
624
580
|
# /^router bgp #{ASN}/)
|
|
625
581
|
def find_ascii(body, regex_query, *parent_cfg)
|
|
626
|
-
return nil if body.nil?
|
|
582
|
+
return nil if body.nil? || regex_query.nil?
|
|
627
583
|
|
|
628
584
|
# get subconfig
|
|
629
585
|
parent_cfg.each { |p| body = find_subconfig(body, p) }
|
|
630
|
-
if body.nil?
|
|
586
|
+
if body.nil? || body.empty?
|
|
631
587
|
return nil
|
|
632
588
|
else
|
|
633
589
|
# find matches and return as array of String if it only does one
|
|
@@ -635,7 +591,7 @@ module Cisco
|
|
|
635
591
|
match = body.split("\n").map { |s| s.scan(regex_query) }
|
|
636
592
|
match = match.flatten(1)
|
|
637
593
|
return nil if match.empty?
|
|
638
|
-
match = match.flatten if match[0].is_a?(Array)
|
|
594
|
+
match = match.flatten if match[0].is_a?(Array) && match[0].length == 1
|
|
639
595
|
return match
|
|
640
596
|
end
|
|
641
597
|
end
|
|
@@ -649,16 +605,16 @@ module Cisco
|
|
|
649
605
|
# @return [String, nil] the subsection of body, de-indented
|
|
650
606
|
# appropriately, or nil if no such subsection exists.
|
|
651
607
|
def find_subconfig(body, regex_query)
|
|
652
|
-
return nil if body.nil?
|
|
608
|
+
return nil if body.nil? || regex_query.nil?
|
|
653
609
|
|
|
654
610
|
rows = body.split("\n")
|
|
655
611
|
match_row_index = rows.index { |row| regex_query =~ row }
|
|
656
612
|
return nil if match_row_index.nil?
|
|
657
613
|
|
|
658
|
-
cur = match_row_index+1
|
|
614
|
+
cur = match_row_index + 1
|
|
659
615
|
subconfig = []
|
|
660
616
|
|
|
661
|
-
until (/\A\s+.*/ =~ rows[cur]).nil?
|
|
617
|
+
until (/\A\s+.*/ =~ rows[cur]).nil? || cur == rows.length
|
|
662
618
|
subconfig << rows[cur]
|
|
663
619
|
cur += 1
|
|
664
620
|
end
|