vtysh 0.1.0 → 0.2.1
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 +4 -4
- data/lib/vtysh/diff.rb +173 -270
- data/lib/vtysh/version.rb +1 -1
- metadata +3 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 34af7b512a21982b0dc323bbfc0552909980573621430434dea7906f84c2cf84
|
|
4
|
+
data.tar.gz: 9b087692d112485f5dd6adcdff7ac1d9915e4276036c88ecba98860e495e16bb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4ebe4a05ea96de7d365b4171b97d47e1544b1c6b23c0a81eba074d884af669b6e753732cf5d606aa72bf346d0e382229f913a25401a2328b0c3f4b0aa68d9bcc
|
|
7
|
+
data.tar.gz: 4eaa1c86dc0a01e5ba84c4515b8d9ff216511c015a9f4fb0ce865b95f8e6e83eb49ce2a16cfcb08b595ec89ba600bc603dd717c9d21562e0923bd576e4b96972
|
data/lib/vtysh/diff.rb
CHANGED
|
@@ -1,324 +1,227 @@
|
|
|
1
1
|
module Vtysh
|
|
2
2
|
class Diff
|
|
3
3
|
def self.commands(source, target)
|
|
4
|
-
# Parse configurations into flat commands
|
|
5
4
|
source_cmds = parse_config(source)
|
|
6
5
|
target_cmds = parse_config(target)
|
|
7
|
-
|
|
8
|
-
# Generate commands to transform source to target
|
|
9
|
-
commands = []
|
|
10
|
-
|
|
11
|
-
# Special case for router-id changes
|
|
6
|
+
|
|
12
7
|
if needs_bgp_recreation?(source_cmds, target_cmds)
|
|
13
|
-
|
|
8
|
+
bgp_commands = handle_bgp_recreation(source_cmds, target_cmds)
|
|
9
|
+
non_bgp_commands = handle_non_bgp_changes(source_cmds, target_cmds, skip_bgp_blocks: true)
|
|
10
|
+
bgp_commands + non_bgp_commands
|
|
14
11
|
else
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if is_block_command(cmd[:command])
|
|
18
|
-
# For block commands like "router bgp X", we need to remove the whole block
|
|
19
|
-
commands << format_removal_command(cmd)
|
|
20
|
-
|
|
21
|
-
# Skip commands inside this block - they'll be removed automatically
|
|
22
|
-
if cmd[:command].start_with?("router bgp")
|
|
23
|
-
# We need to specifically exclude commands inside this removed BGP block
|
|
24
|
-
# to avoid generating individual "no" commands for them
|
|
25
|
-
asn = cmd[:command].split[2]
|
|
26
|
-
# Don't generate commands for items in this block
|
|
27
|
-
end
|
|
28
|
-
elsif cmd[:depth] > 0
|
|
29
|
-
# Check if we're removing a command inside a block that's already being removed
|
|
30
|
-
block_being_removed = false
|
|
31
|
-
cmd[:context].each do |ctx|
|
|
32
|
-
# Check if any of the contexts is a block that's being removed
|
|
33
|
-
if source_cmds.any? { |s| s[:command] == ctx && (source_cmds - target_cmds).include?(s) }
|
|
34
|
-
block_being_removed = true
|
|
35
|
-
break
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Only generate a command if not inside a block that's being removed
|
|
40
|
-
unless block_being_removed
|
|
41
|
-
# For commands inside blocks, we need to provide the proper context
|
|
42
|
-
commands << format_context_command("no #{cmd[:command]}", cmd[:context])
|
|
43
|
-
end
|
|
44
|
-
else
|
|
45
|
-
# For top-level commands
|
|
46
|
-
commands << "vtysh -c \"configure\" -c \"no #{cmd[:command]}\""
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Handle additions (items in target but not in source)
|
|
51
|
-
(target_cmds - source_cmds).each do |cmd|
|
|
52
|
-
if is_block_command(cmd[:command])
|
|
53
|
-
# For block commands like "router bgp X", include the block creation
|
|
54
|
-
commands << "vtysh -c \"configure\" -c \"#{cmd[:command]}\""
|
|
55
|
-
elsif cmd[:depth] > 0
|
|
56
|
-
# For commands inside blocks, we need to provide the proper context
|
|
57
|
-
commands << format_context_command(cmd[:command], cmd[:context])
|
|
58
|
-
else
|
|
59
|
-
# For top-level commands
|
|
60
|
-
commands << "vtysh -c \"configure\" -c \"#{cmd[:command]}\""
|
|
61
|
-
end
|
|
62
|
-
end
|
|
12
|
+
handle_non_bgp_changes(source_cmds, target_cmds) +
|
|
13
|
+
handle_incremental_bgp_changes(source_cmds, target_cmds)
|
|
63
14
|
end
|
|
64
|
-
|
|
65
|
-
# Sort commands and apply dependency-based reordering
|
|
66
|
-
reorder_commands(commands.uniq)
|
|
67
15
|
end
|
|
68
|
-
|
|
16
|
+
|
|
69
17
|
private
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
final_commands.concat(prefix_list_cmds)
|
|
87
|
-
|
|
88
|
-
# Handle route-map definitions second
|
|
89
|
-
route_map_cmds = clean_commands.select { |cmd| cmd.include?("route-map") && !cmd.include?("neighbor") && !cmd.include?("no ") }
|
|
90
|
-
final_commands.concat(route_map_cmds)
|
|
91
|
-
|
|
92
|
-
# Include router bgp blocks
|
|
93
|
-
bgp_block_cmds = clean_commands.select { |cmd| cmd.include?("router bgp") && !cmd.include?("no ") && cmd.count("\"") <= 6 }
|
|
94
|
-
final_commands.concat(bgp_block_cmds)
|
|
95
|
-
|
|
96
|
-
# Now add peer-group commands first
|
|
97
|
-
all_peer_group_cmds.sort.each do |cmd|
|
|
98
|
-
final_commands << cmd
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Then add remote-as commands
|
|
102
|
-
all_remote_as_cmds.sort.each do |cmd|
|
|
103
|
-
final_commands << cmd
|
|
18
|
+
|
|
19
|
+
def self.handle_non_bgp_changes(source_cmds, target_cmds, skip_bgp_blocks: false)
|
|
20
|
+
commands = []
|
|
21
|
+
|
|
22
|
+
# Removals (non-BGP, plus top-level BGP block removals when not recreating)
|
|
23
|
+
(source_cmds - target_cmds).each do |cmd|
|
|
24
|
+
next if inside_bgp?(cmd) && !cmd[:command].start_with?("router bgp")
|
|
25
|
+
next if skip_bgp_blocks && cmd[:command].start_with?("router bgp")
|
|
26
|
+
if is_block_command(cmd[:command])
|
|
27
|
+
commands << format_removal_command(cmd)
|
|
28
|
+
elsif cmd[:depth] > 0
|
|
29
|
+
next if block_being_removed?(cmd, source_cmds, target_cmds)
|
|
30
|
+
commands << format_context_command("no #{cmd[:command]}", cmd[:context])
|
|
31
|
+
else
|
|
32
|
+
commands << vtysh_cmd("no #{cmd[:command]}")
|
|
33
|
+
end
|
|
104
34
|
end
|
|
105
|
-
|
|
106
|
-
#
|
|
107
|
-
|
|
108
|
-
|
|
35
|
+
|
|
36
|
+
# Non-BGP additions
|
|
37
|
+
(target_cmds - source_cmds).each do |cmd|
|
|
38
|
+
next if inside_bgp?(cmd)
|
|
39
|
+
if is_block_command(cmd[:command])
|
|
40
|
+
commands << vtysh_cmd(cmd[:command])
|
|
41
|
+
elsif cmd[:depth] > 0
|
|
42
|
+
commands << format_context_command(cmd[:command], cmd[:context])
|
|
43
|
+
else
|
|
44
|
+
commands << vtysh_cmd(cmd[:command])
|
|
45
|
+
end
|
|
109
46
|
end
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
remaining_cmds = clean_commands.select { |cmd| !cmd.include?("no ") }
|
|
113
|
-
remaining_cmds -= final_commands
|
|
114
|
-
final_commands.concat(remaining_cmds)
|
|
115
|
-
|
|
116
|
-
# Add removal commands at the end
|
|
117
|
-
removal_cmds = clean_commands.select { |cmd| cmd.include?("no ") }
|
|
118
|
-
final_commands.concat(removal_cmds)
|
|
119
|
-
|
|
120
|
-
# Return unique commands with duplicates removed
|
|
121
|
-
final_commands.uniq
|
|
47
|
+
|
|
48
|
+
reorder_non_bgp(commands)
|
|
122
49
|
end
|
|
123
|
-
|
|
124
|
-
def self.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
cmd[:depth] > 0 &&
|
|
141
|
-
cmd[:context].any? { |ctx| ctx.start_with?("router bgp #{asn}") } &&
|
|
142
|
-
cmd[:command].include?("bgp router-id")
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
# Find router-id in target
|
|
146
|
-
tgt_bgp_cmds = target_cmds.select { |cmd|
|
|
147
|
-
cmd[:depth] > 0 &&
|
|
148
|
-
cmd[:context].any? { |ctx| ctx.start_with?("router bgp #{asn}") } &&
|
|
149
|
-
cmd[:command].include?("bgp router-id")
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
# If both have router-id and they're different, return true
|
|
153
|
-
if !src_bgp_cmds.empty? && !tgt_bgp_cmds.empty? &&
|
|
154
|
-
src_bgp_cmds.first[:command] != tgt_bgp_cmds.first[:command]
|
|
155
|
-
return true
|
|
156
|
-
end
|
|
50
|
+
|
|
51
|
+
def self.handle_incremental_bgp_changes(source_cmds, target_cmds)
|
|
52
|
+
commands = []
|
|
53
|
+
|
|
54
|
+
# BGP removals
|
|
55
|
+
(source_cmds - target_cmds).each do |cmd|
|
|
56
|
+
next unless inside_bgp?(cmd)
|
|
57
|
+
next if cmd[:command].start_with?("router bgp") # don't remove the block itself
|
|
58
|
+
next if block_being_removed?(cmd, source_cmds, target_cmds)
|
|
59
|
+
commands << format_context_command("no #{cmd[:command]}", cmd[:context])
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# BGP additions
|
|
63
|
+
(target_cmds - source_cmds).each do |cmd|
|
|
64
|
+
next unless inside_bgp?(cmd)
|
|
65
|
+
next if cmd[:command].start_with?("router bgp")
|
|
66
|
+
commands << format_context_command(cmd[:command], cmd[:context])
|
|
157
67
|
end
|
|
158
|
-
|
|
159
|
-
|
|
68
|
+
|
|
69
|
+
reorder_bgp(commands)
|
|
160
70
|
end
|
|
161
|
-
|
|
71
|
+
|
|
162
72
|
def self.handle_bgp_recreation(source_cmds, target_cmds)
|
|
73
|
+
source_asns = source_cmds.select { |c| c[:command].start_with?("router bgp") }.map { |c| c[:command].split[2] }
|
|
74
|
+
target_asns = target_cmds.select { |c| c[:command].start_with?("router bgp") }.map { |c| c[:command].split[2] }
|
|
75
|
+
|
|
163
76
|
commands = []
|
|
164
|
-
|
|
165
|
-
# Get BGP ASNs
|
|
166
|
-
source_bgp = source_cmds.select { |cmd| cmd[:command].start_with?("router bgp") }
|
|
167
|
-
target_bgp = target_cmds.select { |cmd| cmd[:command].start_with?("router bgp") }
|
|
168
|
-
|
|
169
|
-
source_asns = source_bgp.map { |cmd| cmd[:command].split[2] }
|
|
170
|
-
target_asns = target_bgp.map { |cmd| cmd[:command].split[2] }
|
|
171
|
-
|
|
172
|
-
# For each ASN, recreate the BGP block
|
|
77
|
+
|
|
173
78
|
(source_asns & target_asns).each do |asn|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
79
|
+
bgp_ctx = "router bgp #{asn}"
|
|
80
|
+
|
|
81
|
+
# Step 1: Remove the old BGP block
|
|
82
|
+
commands << vtysh_cmd("no #{bgp_ctx}")
|
|
83
|
+
|
|
84
|
+
# Step 2: Create the new BGP block
|
|
85
|
+
commands << vtysh_cmd(bgp_ctx)
|
|
86
|
+
|
|
87
|
+
# Step 3: Add main BGP commands (not inside address-family)
|
|
88
|
+
target_bgp = target_cmds.select { |c|
|
|
89
|
+
c[:depth] > 0 &&
|
|
90
|
+
c[:context].include?(bgp_ctx) &&
|
|
91
|
+
!c[:context].any? { |ctx| ctx.start_with?("address-family") } &&
|
|
92
|
+
!is_block_command(c[:command]) # skip block commands (router bgp, address-family)
|
|
180
93
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
# Add commands within the BGP block
|
|
186
|
-
bgp_main_cmds = target_bgp_block.select { |cmd|
|
|
187
|
-
!cmd[:context].any? { |ctx| ctx.start_with?("address-family") }
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
# Sort commands - router-id first, then peer-groups, then remote-as, etc.
|
|
191
|
-
bgp_main_cmds.sort_by! { |cmd| bgp_command_priority(cmd[:command]) }
|
|
192
|
-
|
|
193
|
-
# Add the main BGP commands
|
|
194
|
-
bgp_main_cmds.each do |cmd|
|
|
195
|
-
commands << "vtysh -c \"configure\" -c \"router bgp #{asn}\" -c \"#{cmd[:command]}\""
|
|
94
|
+
|
|
95
|
+
target_bgp.sort_by { |c| bgp_command_priority(c[:command]) }.each do |cmd|
|
|
96
|
+
commands << vtysh_cmd(bgp_ctx, cmd[:command])
|
|
196
97
|
end
|
|
197
|
-
|
|
198
|
-
# Add address-family blocks
|
|
199
|
-
af_contexts =
|
|
200
|
-
|
|
201
|
-
}.
|
|
202
|
-
|
|
203
|
-
af_contexts.each do |
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
98
|
+
|
|
99
|
+
# Step 4: Add address-family blocks
|
|
100
|
+
af_contexts = target_cmds.select { |c|
|
|
101
|
+
c[:command].start_with?("address-family") && c[:context].include?(bgp_ctx)
|
|
102
|
+
}.map { |c| c[:command] }.uniq
|
|
103
|
+
|
|
104
|
+
af_contexts.each do |af|
|
|
105
|
+
commands << vtysh_cmd(bgp_ctx, af)
|
|
106
|
+
|
|
107
|
+
af_cmds = target_cmds.select { |c|
|
|
108
|
+
c[:depth] > 0 &&
|
|
109
|
+
c[:context].include?(bgp_ctx) &&
|
|
110
|
+
c[:context].include?(af) &&
|
|
111
|
+
c[:command] != af # skip the address-family command itself
|
|
112
|
+
}
|
|
113
|
+
|
|
209
114
|
af_cmds.each do |cmd|
|
|
210
|
-
commands <<
|
|
115
|
+
commands << vtysh_cmd(bgp_ctx, af, cmd[:command])
|
|
211
116
|
end
|
|
212
117
|
end
|
|
213
118
|
end
|
|
214
|
-
|
|
119
|
+
|
|
215
120
|
commands
|
|
216
121
|
end
|
|
217
|
-
|
|
122
|
+
|
|
123
|
+
# --- Helpers ---
|
|
124
|
+
|
|
125
|
+
def self.inside_bgp?(cmd)
|
|
126
|
+
cmd[:context].any? { |ctx| ctx.start_with?("router bgp") } ||
|
|
127
|
+
cmd[:command].start_with?("router bgp")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def self.block_being_removed?(cmd, source_cmds, target_cmds)
|
|
131
|
+
removals = source_cmds - target_cmds
|
|
132
|
+
cmd[:context].any? { |ctx|
|
|
133
|
+
is_block_command(ctx) && removals.any? { |r| r[:command] == ctx }
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def self.needs_bgp_recreation?(source_cmds, target_cmds)
|
|
138
|
+
source_asns = source_cmds.select { |c| c[:command].start_with?("router bgp") }.map { |c| c[:command].split[2] }
|
|
139
|
+
target_asns = target_cmds.select { |c| c[:command].start_with?("router bgp") }.map { |c| c[:command].split[2] }
|
|
140
|
+
|
|
141
|
+
return false if source_asns != target_asns || source_asns.empty?
|
|
142
|
+
|
|
143
|
+
source_asns.any? do |asn|
|
|
144
|
+
src_rid = source_cmds.find { |c| c[:command].include?("bgp router-id") && c[:context].any? { |ctx| ctx.include?("router bgp #{asn}") } }
|
|
145
|
+
tgt_rid = target_cmds.find { |c| c[:command].include?("bgp router-id") && c[:context].any? { |ctx| ctx.include?("router bgp #{asn}") } }
|
|
146
|
+
src_rid && tgt_rid && src_rid[:command] != tgt_rid[:command]
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
218
150
|
def self.bgp_command_priority(cmd)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
elsif cmd.include?('bgp listen range')
|
|
227
|
-
return 4 # bgp listen range commands after
|
|
228
|
-
else
|
|
229
|
-
return 5 # other commands last
|
|
151
|
+
case
|
|
152
|
+
when cmd.include?('bgp router-id') then 1
|
|
153
|
+
when cmd =~ /neighbor \S+ peer-group$/ then 2
|
|
154
|
+
when cmd.include?('remote-as') then 3
|
|
155
|
+
when cmd =~ /neighbor \S+ local-as/ then 4
|
|
156
|
+
when cmd.include?('bgp listen range') then 5
|
|
157
|
+
else 6
|
|
230
158
|
end
|
|
231
159
|
end
|
|
232
|
-
|
|
160
|
+
|
|
161
|
+
def self.reorder_non_bgp(commands)
|
|
162
|
+
prefix_lists = commands.select { |c| c.include?("ip prefix-list") }
|
|
163
|
+
route_maps = commands.select { |c| c.include?("route-map") && !c.include?("no ") }
|
|
164
|
+
removals = commands.select { |c| c.include?("no ") } - prefix_lists
|
|
165
|
+
rest = commands - prefix_lists - route_maps - removals
|
|
166
|
+
|
|
167
|
+
(prefix_lists + route_maps + rest + removals).uniq
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def self.reorder_bgp(commands)
|
|
171
|
+
peer_groups = commands.select { |c| c =~ /neighbor \S+ peer-group"$/ && !c.include?("no ") }
|
|
172
|
+
remote_as = commands.select { |c| c.include?("remote-as") && !c.include?("no ") }
|
|
173
|
+
listen_range = commands.select { |c| c.include?("bgp listen range") && !c.include?("no ") }
|
|
174
|
+
removals = commands.select { |c| c.include?("no ") }
|
|
175
|
+
rest = commands - peer_groups - remote_as - listen_range - removals
|
|
176
|
+
|
|
177
|
+
(peer_groups + remote_as + listen_range + rest + removals).uniq
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# --- Parsing ---
|
|
181
|
+
|
|
233
182
|
def self.parse_config(config)
|
|
234
|
-
lines = config.split("\n").map(&:strip)
|
|
235
183
|
commands = []
|
|
236
184
|
context_stack = []
|
|
237
|
-
|
|
238
|
-
|
|
185
|
+
|
|
186
|
+
config.each_line do |line|
|
|
187
|
+
line = line.strip
|
|
239
188
|
next if line.empty? || line.start_with?('#', '!')
|
|
240
|
-
|
|
241
|
-
if line
|
|
242
|
-
|
|
189
|
+
next if line.start_with?('ip route ') # managed by config_db, not vtysh
|
|
190
|
+
next if line =~ /^frr (version|defaults)/ || line == 'no service integrated-vtysh-config' || line.start_with?('agentx')
|
|
191
|
+
|
|
192
|
+
if line =~ /^exit(-address-family|-vrf)?$/
|
|
243
193
|
context_stack.pop unless context_stack.empty?
|
|
244
194
|
elsif line == 'end'
|
|
245
|
-
# Exit all contexts
|
|
246
195
|
context_stack = []
|
|
247
196
|
elsif is_block_command(line)
|
|
248
|
-
# Start a new context block
|
|
249
197
|
context_stack << line
|
|
250
198
|
commands << { command: line, context: context_stack.dup, depth: context_stack.size }
|
|
251
199
|
else
|
|
252
|
-
# Regular command, may be inside a context
|
|
253
200
|
commands << { command: line, context: context_stack.dup, depth: context_stack.size }
|
|
254
201
|
end
|
|
255
202
|
end
|
|
256
|
-
|
|
203
|
+
|
|
257
204
|
commands
|
|
258
205
|
end
|
|
259
|
-
|
|
206
|
+
|
|
260
207
|
def self.is_block_command(cmd)
|
|
261
208
|
cmd.start_with?('router ', 'interface ', 'route-map ', 'vrf ', 'address-family ')
|
|
262
209
|
end
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
210
|
+
|
|
211
|
+
# --- Formatting ---
|
|
212
|
+
|
|
213
|
+
def self.vtysh_cmd(*parts)
|
|
214
|
+
args = ["configure"] + parts
|
|
215
|
+
"vtysh " + args.map { |p| "-c \"#{p}\"" }.join(" ")
|
|
266
216
|
end
|
|
267
|
-
|
|
217
|
+
|
|
268
218
|
def self.format_removal_command(cmd)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
# For block commands with nested structures
|
|
272
|
-
if command.start_with?('router bgp')
|
|
273
|
-
asn = command.split[2]
|
|
274
|
-
return "vtysh -c \"configure\" -c \"no router bgp #{asn}\""
|
|
275
|
-
elsif command.start_with?('interface')
|
|
276
|
-
iface = command.split[1]
|
|
277
|
-
return "vtysh -c \"configure\" -c \"no interface #{iface}\""
|
|
278
|
-
elsif command.start_with?('vrf')
|
|
279
|
-
vrf_name = command.split[1]
|
|
280
|
-
return "vtysh -c \"configure\" -c \"no vrf #{vrf_name}\""
|
|
281
|
-
elsif command.start_with?('route-map')
|
|
282
|
-
parts = command.split
|
|
283
|
-
if parts.size >= 4
|
|
284
|
-
return "vtysh -c \"configure\" -c \"no route-map #{parts[1]} #{parts[2]} #{parts[3]}\""
|
|
285
|
-
else
|
|
286
|
-
return "vtysh -c \"configure\" -c \"no #{command}\""
|
|
287
|
-
end
|
|
288
|
-
else
|
|
289
|
-
# Generic block removal
|
|
290
|
-
return "vtysh -c \"configure\" -c \"no #{command}\""
|
|
291
|
-
end
|
|
219
|
+
vtysh_cmd("no #{cmd[:command]}")
|
|
292
220
|
end
|
|
293
|
-
|
|
221
|
+
|
|
294
222
|
def self.format_context_command(command, context)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
# Add each context element
|
|
298
|
-
context.each do |ctx|
|
|
299
|
-
if ctx.start_with?('router bgp')
|
|
300
|
-
asn = ctx.split[2]
|
|
301
|
-
result += " -c \"router bgp #{asn}\""
|
|
302
|
-
elsif ctx.start_with?('interface')
|
|
303
|
-
iface = ctx.split[1]
|
|
304
|
-
result += " -c \"interface #{iface}\""
|
|
305
|
-
elsif ctx.start_with?('address-family')
|
|
306
|
-
parts = ctx.split
|
|
307
|
-
if parts.size > 2
|
|
308
|
-
result += " -c \"address-family #{parts[1]} #{parts[2]}\""
|
|
309
|
-
else
|
|
310
|
-
result += " -c \"address-family #{parts[1]}\""
|
|
311
|
-
end
|
|
312
|
-
else
|
|
313
|
-
# Generic context
|
|
314
|
-
result += " -c \"#{ctx}\""
|
|
315
|
-
end
|
|
316
|
-
end
|
|
317
|
-
|
|
318
|
-
# Add the command itself
|
|
319
|
-
result += " -c \"#{command}\""
|
|
320
|
-
|
|
321
|
-
result
|
|
223
|
+
parts = context.map { |ctx| ctx }
|
|
224
|
+
vtysh_cmd(*parts, command)
|
|
322
225
|
end
|
|
323
226
|
end
|
|
324
|
-
end
|
|
227
|
+
end
|
data/lib/vtysh/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: vtysh
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jonathan Siegel
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
12
|
description: A gem that accepts two vtysh configuration states and returns the commands
|
|
14
13
|
needed to transition from one to the other
|
|
@@ -28,7 +27,6 @@ homepage: http://github.com/usiegj00/vtysh-gem
|
|
|
28
27
|
licenses:
|
|
29
28
|
- MIT
|
|
30
29
|
metadata: {}
|
|
31
|
-
post_install_message:
|
|
32
30
|
rdoc_options: []
|
|
33
31
|
require_paths:
|
|
34
32
|
- lib
|
|
@@ -43,8 +41,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
43
41
|
- !ruby/object:Gem::Version
|
|
44
42
|
version: '0'
|
|
45
43
|
requirements: []
|
|
46
|
-
rubygems_version: 3.
|
|
47
|
-
signing_key:
|
|
44
|
+
rubygems_version: 3.6.9
|
|
48
45
|
specification_version: 4
|
|
49
46
|
summary: Handles SONiC vtysh commandfile format
|
|
50
47
|
test_files: []
|