free-range 0.1.2 → 0.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8cef8634d2f2d3e9e90289e4098b5bac3341872844764b0db4bca9e0b3bdaf0e
4
- data.tar.gz: ca753e285e70bce1f8ca7d947c56d76683901666c103765de05a6f58a36c879b
3
+ metadata.gz: 8c353a0a3ea2e3a261a5cb6af72b3e5e4ac847be178a710e79927264e256393a
4
+ data.tar.gz: 771ead5640626c25b48b5639746246a08a44bb2f58db8cca3971581257de97e8
5
5
  SHA512:
6
- metadata.gz: ac8287096aa861556171e3b7ad393640ff4193e16de0d062666f0b576b80b6768aaa96fa47e511ab8b94ce4e0473cec28c7f402b89b5c76f691c72d62e017934
7
- data.tar.gz: 9a2778e40dd3a4e189ebe32bd5e56b0676d08be1bb05f13f9c3b08378d9c9a65638b5f0742bff33991f2e2521dd4549a2b9f112a526226f71889c1fde8116616
6
+ metadata.gz: ef260cde29dfee53e9ab895c14a232c0e023052cb2c614e2ce3e7d34ffbdefebabbe856c2fbaa143179b7fa387468dd32d89718da69ab22d669230fd2a164a76
7
+ data.tar.gz: 2477e3ea176e80ceb40869bf1e2835ffd63b4a8e733fb87d3f4497bcc70d27d74814f94586e946b2c5b24f8db9f7359f4bb60c7a3087078927d608d03234915d
data/lib/free-range.rb CHANGED
@@ -3,7 +3,6 @@ require 'rmagick'
3
3
  require 'fileutils'
4
4
 
5
5
  module FreeRange
6
-
7
6
  # Перевірка наявності ImageMagick
8
7
  begin
9
8
  require 'rmagick'
@@ -20,16 +19,20 @@ module FreeRange
20
19
  end
21
20
 
22
21
  # Додаємо VLAN до списку (віртуальний метод, може бути перевизначений)
22
+ # @param vlan [Integer] VLAN ID to add
23
+ # @return [void]
23
24
  def add_vlan(vlan)
24
25
  @vlans << vlan
25
26
  end
26
27
 
27
28
  # Повертаємо масив VLAN
29
+ # @return [Array<Integer>] Sorted array of unique VLAN IDs
28
30
  def vlans
29
31
  @vlans.uniq.sort
30
32
  end
31
33
 
32
34
  # Створюємо хеш діапазонів із VLAN
35
+ # @return [Hash{Integer => Integer}] Hash mapping range start to range end
33
36
  def ranges
34
37
  return {} if @vlans.empty?
35
38
 
@@ -59,16 +62,23 @@ module FreeRange
59
62
  end
60
63
 
61
64
  # Додаємо діапазон до списку VLAN-ів
65
+ # @param start [Integer] Start of VLAN range
66
+ # @param finish [Integer] End of VLAN range
67
+ # @return [void]
62
68
  def add_range(start, finish)
63
69
  (start..finish).each { |vlan| @vlans << vlan }
64
70
  end
65
71
 
66
72
  # Додаємо діапазон до списку "інших" VLAN-ів
73
+ # @param start [Integer] Start of another VLAN range
74
+ # @param finish [Integer] End of another VLAN range
75
+ # @return [void]
67
76
  def add_another_range(start, finish)
68
77
  (start..finish).each { |vlan| @another_in_ranges << vlan }
69
78
  end
70
79
 
71
80
  # Повертаємо масив "інших" VLAN-ів
81
+ # @return [Array<Integer>] Sorted array of unique "another" VLAN IDs
72
82
  def another_in_ranges
73
83
  @another_in_ranges.uniq.sort
74
84
  end
@@ -84,6 +94,9 @@ module FreeRange
84
94
 
85
95
  # Клас для виводу даних
86
96
  class Print
97
+ # Виводить хеш діапазонів VLAN у порядку зростання
98
+ # @param ranges [Ranges] Object containing VLAN ranges
99
+ # @return [void]
87
100
  def self.ranged(ranges)
88
101
  puts "\nСформований хеш діапазонів (в порядку зростання):"
89
102
  if ranges.ranges.empty?
@@ -95,6 +108,9 @@ module FreeRange
95
108
  end
96
109
  end
97
110
 
111
+ # Виводить зайняті VLAN-и в межах діапазонів у порядку зростання
112
+ # @param vlans [Vlans] Object containing VLANs
113
+ # @return [void]
98
114
  def self.vlans(vlans)
99
115
  puts "\nЗайняті VLAN-и в межах діапазонів (в порядку зростання):"
100
116
  if vlans.vlans.empty?
@@ -104,6 +120,9 @@ module FreeRange
104
120
  end
105
121
  end
106
122
 
123
+ # Виводить діапазони VLAN-ів у порядку зростання
124
+ # @param vlans [Vlans] Object containing VLANs
125
+ # @return [void]
107
126
  def self.vlan_ranges(vlans)
108
127
  puts "\nДіапазони VLAN-ів (в порядку зростання):"
109
128
  vlan_ranges = vlans.vlan_ranges
@@ -116,6 +135,13 @@ module FreeRange
116
135
  end
117
136
  end
118
137
 
138
+ # Виводить комбіновані діапазони VLAN зі статусами
139
+ # @param ranges [Ranges] Object containing VLAN ranges
140
+ # @param vlans [Vlans] Object containing VLANs
141
+ # @param use_color [Boolean] Enable colored output
142
+ # @param target [String] Target device hostname
143
+ # @param interface [String, nil] Interface name or nil
144
+ # @return [void]
119
145
  def self.combined_ranges(ranges, vlans, use_color, target, interface = nil)
120
146
  if ranges.ranges.empty?
121
147
  puts "Не знайдено діапазонів."
@@ -142,6 +168,13 @@ module FreeRange
142
168
  puts result.join(',')
143
169
  end
144
170
 
171
+ # Виводить таблицю розподілу VLAN
172
+ # @param ranges [Ranges] Object containing VLAN ranges
173
+ # @param vlans [Vlans] Object containing VLANs
174
+ # @param use_color [Boolean] Enable colored output
175
+ # @param target [String] Target device hostname
176
+ # @param interface [String, nil] Interface name or nil
177
+ # @return [void]
145
178
  def self.table(ranges, vlans, use_color, target, interface = nil)
146
179
  puts "VLAN Distribution for #{target}#{interface ? " (#{interface})" : ''}"
147
180
  all_vlans, status_counts = build_vlan_statuses(ranges, vlans)
@@ -191,6 +224,13 @@ module FreeRange
191
224
  puts summary_text
192
225
  end
193
226
 
227
+ # Зберігає таблицю розподілу VLAN як PNG-зображення
228
+ # @param ranges [Ranges] Object containing VLAN ranges
229
+ # @param vlans [Vlans] Object containing VLANs
230
+ # @param path [String] Directory path to save the PNG
231
+ # @param target [String] Target device hostname
232
+ # @param interface [String, nil] Interface name or nil
233
+ # @return [void]
194
234
  def self.table_png(ranges, vlans, path, target, interface = nil)
195
235
  all_vlans, status_counts = build_vlan_statuses(ranges, vlans)
196
236
  cell_width = 12
@@ -307,6 +347,10 @@ module FreeRange
307
347
 
308
348
  private
309
349
 
350
+ # Будує хеш статусів VLAN і підраховує кількість кожного статусу
351
+ # @param ranges [Ranges] Object containing VLAN ranges
352
+ # @param vlans [Vlans] Object containing VLANs
353
+ # @return [Array<Hash, Hash>] Hash of VLAN statuses and status counts
310
354
  def self.build_vlan_statuses(ranges, vlans)
311
355
  all_vlans = {}
312
356
  ranges.ranges.each do |start, finish|
@@ -321,6 +365,12 @@ module FreeRange
321
365
  [all_vlans, status_counts]
322
366
  end
323
367
 
368
+ # Форматує діапазон VLAN зі статусом для виводу
369
+ # @param start [Integer] Start of VLAN range
370
+ # @param finish [Integer] End of VLAN range
371
+ # @param status [String] VLAN status ('f', 'b', 'e', 'c', 'a', 'u')
372
+ # @param use_color [Boolean] Enable colored output
373
+ # @return [String] Formatted range string
324
374
  def self.format_range(start, finish, status, use_color)
325
375
  range_text = start == finish ? "#{start}" : "#{start}-#{finish}"
326
376
  range_text_with_status = "#{range_text}(#{status})"
@@ -339,6 +389,10 @@ module FreeRange
339
389
  end
340
390
  end
341
391
 
392
+ # Форматує символ для таблиці VLAN
393
+ # @param status [String] VLAN status ('f', 'b', 'e', 'c', 'a', 'u', or ' ')
394
+ # @param use_color [Boolean] Enable colored output
395
+ # @return [String] Formatted character for table display
342
396
  def self.format_table_char(status, use_color)
343
397
  if use_color
344
398
  case status
@@ -357,6 +411,10 @@ module FreeRange
357
411
  end
358
412
 
359
413
  # Метод для заповнення Ranges для одного інтерфейсу
414
+ # @param ssh_command [String] SSH command to execute
415
+ # @param interface [String, nil] Interface name or nil for all interfaces
416
+ # @param ranges [Ranges] Object to store VLAN ranges
417
+ # @return [void]
360
418
  def self.process_interface(ssh_command, interface, ranges)
361
419
  command_ranges = interface ? "show configuration interfaces #{interface} | no-more | display set | match dynamic-profile | match \"ranges ([0-9]+(-[0-9]+)?)\"" : 'show configuration interfaces | no-more | display set | match dynamic-profile | match "ranges ([0-9]+(-[0-9]+)?)"'
362
420
  command_demux = interface ? "show configuration interfaces #{interface} | display set | match unnumbered-address" : 'show configuration interfaces | display set | match unnumbered-address'
@@ -404,6 +462,7 @@ module FreeRange
404
462
  end
405
463
 
406
464
  # Основна логіка виконання
465
+ # @return [void]
407
466
  def self.run
408
467
  options = {}
409
468
  OptionParser.new do |opts|
@@ -437,12 +496,6 @@ module FreeRange
437
496
  table_png_mode = options[:table_png]
438
497
  interface = options[:interface]
439
498
 
440
- # # Перевірка формату інтерфейсу
441
- # if interface && interface != "all" && interface !~ /^[a-z]+-\d+\/\d+\/\d+$/
442
- # puts "Помилка: некоректна назва інтерфейсу. Використовуйте формат 'xe-0/0/2' або 'all'."
443
- # exit 1
444
- # end
445
-
446
499
  login = { target: ARGV[0], username: username, password: password }
447
500
  target = ARGV[0].split('.')[0]
448
501
  puts "Connecting to device: #{login[:target]}"
@@ -1,68 +1,18 @@
1
- module FreeRange
2
-
3
- require 'optparse'
4
- require 'rmagick'
5
- require 'fileutils'
6
-
7
- # Парсинг аргументів командного рядка
8
- options = {}
9
- OptionParser.new do |opts|
10
- opts.banner = "Використання: ruby free-range.rb <IP-адреса або hostname> [опції]"
11
-
12
- opts.on("-u", "--username USERNAME", "Ім'я користувача для SSH") do |u|
13
- options[:username] = u
14
- end
15
-
16
- opts.on("-p", "--password PASSWORD", "Пароль для SSH") do |p|
17
- options[:password] = p
18
- end
19
-
20
- opts.on("-n", "--no-color", "Вимкнути кольоровий вивід") do
21
- options[:no_color] = true
22
- end
1
+ require 'optparse'
2
+ require 'rmagick'
3
+ require 'fileutils'
23
4
 
24
- opts.on("-d", "--debug", "Увімкнути дебаг-режим") do
25
- options[:debug] = true
26
- end
27
-
28
- opts.on("-t", "--table", "Вивести діаграму розподілу VLAN-ів") do
29
- options[:table] = true
30
- end
31
-
32
- opts.on("-g", "--table-png PATH", "Зберегти діаграму розподілу VLAN-ів як PNG у вказаний каталог") do |path|
33
- options[:table_png] = path
34
- end
35
-
36
- opts.on("-i", "--interface INTERFACE", "Назва інтерфейсу або 'all'") do |i|
37
- options[:interface] = i
38
- end
39
- end.parse!
40
-
41
- # Перевіряємо, чи передано IP-адресу або hostname
42
- if ARGV.empty?
43
- puts "Помилка: потрібно вказати IP-адресу або hostname роутера як аргумент командного рядка."
44
- puts "Використання: ruby free-range.rb <IP-адреса або hostname> [-u|--username USERNAME] [-p|--password PASSWORD] [-n|--no-color] [-d|--debug] [-t|--table] [--table-png PATH] [-i|--interface INTERFACE]"
45
- exit 1
46
- end
47
-
48
- # Визначаємо облікові дані
49
- username = options[:username] || ENV['WHOAMI']
50
- password = options[:password] || ENV['WHATISMYPASSWD']
5
+ module FreeRange
51
6
 
52
- # Перевіряємо наявність облікових даних
53
- if username.nil? || password.nil?
54
- puts "Помилка: необхідно вказати ім'я користувача та пароль."
55
- puts "Використовуйте опції -u|--username і -p|--password або змінні оточення WHOAMI і WHATISMYPASSWD."
7
+ # Перевірка наявності ImageMagick
8
+ begin
9
+ require 'rmagick'
10
+ rescue LoadError
11
+ puts "Помилка: бібліотека rmagick не встановлена або ImageMagick недоступний."
12
+ puts "Встановіть ImageMagick і виконайте: gem install rmagick"
56
13
  exit 1
57
14
  end
58
15
 
59
- # Визначаємо, чи використовувати кольори та дебаг-режим
60
- use_color = !options[:no_color] && ENV['TERM'] && ENV['TERM'] != 'dumb'
61
- debug = options[:debug]
62
- table_mode = options[:table]
63
- table_png_mode = options[:table_png]
64
- interface = options[:interface]
65
-
66
16
  # Абстрактний клас для роботи з VLAN
67
17
  class VlanContainer
68
18
  def initialize
@@ -173,8 +123,6 @@ module FreeRange
173
123
  end
174
124
 
175
125
  all_vlans, _status_counts = build_vlan_statuses(ranges, vlans)
176
-
177
- # Формуємо діапазони з урахуванням статусів
178
126
  result = []
179
127
  sorted_vlans = all_vlans.keys.sort
180
128
  start = sorted_vlans.first
@@ -183,14 +131,12 @@ module FreeRange
183
131
 
184
132
  sorted_vlans[1..-1].each do |vlan|
185
133
  unless vlan == prev + 1 && all_vlans[vlan] == status
186
- # Завершуємо попередній діапазон
187
134
  result << format_range(start, prev, status, use_color)
188
135
  start = vlan
189
136
  status = all_vlans[vlan]
190
137
  end
191
138
  prev = vlan
192
139
  end
193
- # Додаємо останній діапазон
194
140
  result << format_range(start, prev, status, use_color)
195
141
 
196
142
  puts result.join(',')
@@ -200,7 +146,6 @@ module FreeRange
200
146
  puts "VLAN Distribution for #{target}#{interface ? " (#{interface})" : ''}"
201
147
  all_vlans, status_counts = build_vlan_statuses(ranges, vlans)
202
148
 
203
- # Виводимо заголовок
204
149
  puts " 0 1 2 3 4 5 6 7 8 9 "
205
150
  (0..40).each do |h|
206
151
  start_vlan = h * 100
@@ -209,7 +154,6 @@ module FreeRange
209
154
  puts "#{format("%4d", start_vlan)} #{row}"
210
155
  end
211
156
 
212
- # Виводимо легенду
213
157
  legend_parts = [
214
158
  ["Legend: ", nil],
215
159
  ["f", 'f'], ["=free", nil], [", ", nil],
@@ -228,7 +172,6 @@ module FreeRange
228
172
  end.join
229
173
  puts "\n#{legend_text}"
230
174
 
231
- # Виводимо підсумок
232
175
  summary_parts = [
233
176
  ["Total: ", nil],
234
177
  ["f", 'f'], ["=#{status_counts['f']}", nil], [", ", nil],
@@ -250,8 +193,6 @@ module FreeRange
250
193
 
251
194
  def self.table_png(ranges, vlans, path, target, interface = nil)
252
195
  all_vlans, status_counts = build_vlan_statuses(ranges, vlans)
253
-
254
- # Налаштування розмірів і стилів
255
196
  cell_width = 12
256
197
  cell_height = 20
257
198
  rows = 41
@@ -259,42 +200,35 @@ module FreeRange
259
200
  header_height = 60
260
201
  label_width = 50
261
202
  width = label_width + cols * cell_width + 10
262
- height = header_height + rows * cell_height + 20 + 50 # Вистачає для легенди і підсумку
203
+ height = header_height + rows * cell_height + 20 + 50
263
204
  font_size = 14
264
- title_font_size = 18 # Більший шрифт для заголовка
205
+ title_font_size = 18
265
206
  font = 'Courier'
266
207
 
267
- # Створюємо полотно
268
208
  canvas = Magick::Image.new(width, height) { |options| options.background_color = 'white' }
269
209
  gc = Magick::Draw.new
270
210
  gc.font = font
271
211
  gc.pointsize = font_size
272
212
  gc.text_antialias = true
273
213
 
274
- # Малюємо заголовок із назвою пристрою
275
214
  gc.fill('black')
276
215
  gc.pointsize = title_font_size
277
216
  gc.text(10, 25, "VLAN Distribution for #{target}#{interface ? " (#{interface})" : ''}")
278
- gc.pointsize = font_size # Повертаємо стандартний розмір шрифту
217
+ gc.pointsize = font_size
279
218
 
280
- # Малюємо заголовок (0 1 2 ... 9)
281
219
  (0..9).each do |i|
282
220
  x = label_width + i * 10 * cell_width - 3
283
221
  gc.fill('black')
284
222
  gc.text(x + 5, header_height - 5, i.to_s)
285
223
  end
286
224
 
287
- # Малюємо таблицю
288
225
  (0..40).each do |h|
289
226
  start_vlan = h * 100
290
227
  end_vlan = [start_vlan + 99, 4094].min
291
228
  y = header_height + h * cell_height
292
-
293
- # Малюємо номер рядка
294
229
  gc.fill('black')
295
230
  gc.text(5, y + font_size, format("%4d", start_vlan))
296
231
 
297
- # Малюємо клітинки
298
232
  (start_vlan..end_vlan).each_with_index do |vlan, i|
299
233
  status = all_vlans[vlan] || ' '
300
234
  x = label_width + i * cell_width
@@ -314,7 +248,6 @@ module FreeRange
314
248
  end
315
249
  end
316
250
 
317
- # Малюємо легенду з кольоровими фонами
318
251
  legend_y = height - 50
319
252
  x = 10
320
253
  legend_parts = [
@@ -336,11 +269,10 @@ module FreeRange
336
269
  else
337
270
  gc.fill('black')
338
271
  gc.text(x, legend_y, text)
339
- x += text.length * 8 # Приблизно 8 пікселів на символ
272
+ x += text.length * 8
340
273
  end
341
274
  end
342
275
 
343
- # Малюємо підсумок VLAN-ів з кольоровими фонами
344
276
  summary_y = height - 30
345
277
  x = 10
346
278
  summary_parts = [
@@ -362,11 +294,10 @@ module FreeRange
362
294
  else
363
295
  gc.fill('black')
364
296
  gc.text(x, summary_y, text)
365
- x += text.length * 8 # Приблизно 8 пікселів на символ
297
+ x += text.length * 8
366
298
  end
367
299
  end
368
300
 
369
- # Зберігаємо зображення
370
301
  gc.draw(canvas)
371
302
  FileUtils.mkdir_p(path) unless Dir.exist?(path)
372
303
  filename = File.join(path, "free-range-#{target}#{interface ? "-#{interface.tr('/', '-')}" : ''}.png")
@@ -378,17 +309,12 @@ module FreeRange
378
309
 
379
310
  def self.build_vlan_statuses(ranges, vlans)
380
311
  all_vlans = {}
381
- # Позначаємо VLAN із діапазонів як free
382
312
  ranges.ranges.each do |start, finish|
383
313
  (start..finish).each { |vlan| all_vlans[vlan] = 'f' }
384
314
  end
385
- # Оновлюємо статуси для зайнятих VLAN
386
315
  vlans.vlans.uniq.each { |vlan| all_vlans[vlan] = all_vlans.key?(vlan) ? 'b' : 'e' }
387
- # Позначаємо всі інші VLAN як unused
388
316
  (1..4094).each { |vlan| all_vlans[vlan] = 'u' unless all_vlans.key?(vlan) }
389
- # Оновлюємо статуси для "інших" VLAN
390
317
  ranges.another_in_ranges.each { |vlan| all_vlans[vlan] = all_vlans.key?(vlan) && all_vlans[vlan] != 'u' ? 'c' : 'a' }
391
- # Підраховуємо статуси
392
318
  status_counts = { 'f' => 0, 'b' => 0, 'e' => 0, 'c' => 0, 'a' => 0, 'u' => 0 }
393
319
  all_vlans.each_value { |status| status_counts[status] += 1 }
394
320
 
@@ -398,23 +324,15 @@ module FreeRange
398
324
  def self.format_range(start, finish, status, use_color)
399
325
  range_text = start == finish ? "#{start}" : "#{start}-#{finish}"
400
326
  range_text_with_status = "#{range_text}(#{status})"
401
-
402
327
  if use_color
403
328
  case status
404
- when 'f'
405
- "\e[32m#{range_text}\e[0m" # Зелений для free
406
- when 'b'
407
- "\e[33m#{range_text}\e[0m" # Жовтий для busy
408
- when 'e'
409
- "\e[31m#{range_text}\e[0m" # Червоний для error
410
- when 'c'
411
- "\e[35m#{range_text}\e[0m" # Фіолетовий для configured
412
- when 'a'
413
- "\e[34m#{range_text}\e[0m" # Синій для another
414
- when 'u'
415
- "\e[90m#{range_text}\e[0m" # Темно-сірий для unused
416
- else
417
- range_text # Без кольору для інших статусів
329
+ when 'f' then "\e[32m#{range_text}\e[0m" # Зелений для free
330
+ when 'b' then "\e[33m#{range_text}\e[0m" # Жовтий для busy
331
+ when 'e' then "\e[31m#{range_text}\e[0m" # Червоний для error
332
+ when 'c' then "\e[35m#{range_text}\e[0m" # Фіолетовий для configured
333
+ when 'a' then "\e[34m#{range_text}\e[0m" # Синій для another
334
+ when 'u' then "\e[90m#{range_text}\e[0m" # Темно-сірий для unused
335
+ else range_text # Без кольору для інших статусів
418
336
  end
419
337
  else
420
338
  range_text_with_status # Текстовий вивід зі статусами
@@ -424,20 +342,13 @@ module FreeRange
424
342
  def self.format_table_char(status, use_color)
425
343
  if use_color
426
344
  case status
427
- when 'f'
428
- "\e[48;5;2m\e[30m#{status}\e[0m" # Зелений фон, чорний текст
429
- when 'b'
430
- "\e[48;5;3m\e[30m#{status}\e[0m" # Жовтий фон, чорний текст
431
- when 'e'
432
- "\e[48;5;1m\e[30m#{status}\e[0m" # Червоний фон, чорний текст
433
- when 'c'
434
- "\e[48;5;5m\e[30m#{status}\e[0m" # Фіолетовий фон, чорний текст
435
- when 'a'
436
- "\e[48;5;4m\e[30m#{status}\e[0m" # Синій фон, чорний текст
437
- when 'u'
438
- "\e[48;5;8m\e[30m#{status}\e[0m" # Темно-сірий фон, чорний текст
439
- else
440
- status # Без кольору для інших статусів
345
+ when 'f' then "\e[48;5;2m\e[30m#{status}\e[0m" # Зелений фон, чорний текст
346
+ when 'b' then "\e[48;5;3m\e[30m#{status}\e[0m" # Жовтий фон, чорний текст
347
+ when 'e' then "\e[48;5;1m\e[30m#{status}\e[0m" # Червоний фон, чорний текст
348
+ when 'c' then "\e[48;5;5m\e[30m#{status}\e[0m" # Фіолетовий фон, чорний текст
349
+ when 'a' then "\e[48;5;4m\e[30m#{status}\e[0m" # Синій фон, чорний текст
350
+ when 'u' then "\e[48;5;8m\e[30m#{status}\e[0m" # Темно-сірий фон, чорний текст
351
+ else status # Без кольору для інших статусів
441
352
  end
442
353
  else
443
354
  status # Текстовий вивід без кольорів
@@ -445,25 +356,14 @@ module FreeRange
445
356
  end
446
357
  end
447
358
 
448
- # Основна логіка
449
- login = { target: ARGV[0], username: username, password: password }
450
- target = ARGV[0].split('.')[0] # Обрізаємо суфікс (наприклад, rhoh15-1.ukrhub.net → rhoh15-1)
451
-
452
- puts "Connecting to device: #{login[:target]}"
453
-
454
- ssh_command = "sshpass -p \"#{login[:password]}\" ssh -C -x -4 -o StrictHostKeyChecking=no #{login[:username]}@#{login[:target]}"
455
- subscribers_command = "ssh -C -x roffice /usr/local/share/noc/bin/radius-subscribers"
456
-
457
- # Функція для заповнення Ranges для одного інтерфейсу
458
- def process_interface(ssh_command, interface, ranges)
359
+ # Метод для заповнення Ranges для одного інтерфейсу
360
+ def self.process_interface(ssh_command, interface, ranges)
459
361
  command_ranges = interface ? "show configuration interfaces #{interface} | no-more | display set | match dynamic-profile | match \"ranges ([0-9]+(-[0-9]+)?)\"" : 'show configuration interfaces | no-more | display set | match dynamic-profile | match "ranges ([0-9]+(-[0-9]+)?)"'
460
362
  command_demux = interface ? "show configuration interfaces #{interface} | display set | match unnumbered-address" : 'show configuration interfaces | display set | match unnumbered-address'
461
363
  command_another = interface ? "show configuration interfaces #{interface} | display set | match vlan-id" : 'show configuration interfaces | display set | match vlan-id'
462
364
 
463
- # Виконуємо команду для отримання діапазонів
464
365
  full_cmd = "#{ssh_command} '#{command_ranges}'"
465
366
  result = `#{full_cmd}`.strip
466
-
467
367
  unless result.empty?
468
368
  result.each_line do |line|
469
369
  if line =~ /ranges (\d+)(?:-(\d+))?/
@@ -474,10 +374,8 @@ module FreeRange
474
374
  end
475
375
  end
476
376
 
477
- # Виконуємо команду для отримання unnumbered-address інтерфейсів (demux-source)
478
377
  full_cmd = "#{ssh_command} '#{command_demux}'"
479
378
  result = `#{full_cmd}`.strip
480
-
481
379
  unless result.empty?
482
380
  result.each_line do |line|
483
381
  if line =~ /unit (\d+)/
@@ -490,10 +388,8 @@ module FreeRange
490
388
  end
491
389
  end
492
390
 
493
- # Виконуємо команду для отримання vlan-ів усіх наявних інтерфейсів
494
391
  full_cmd = "#{ssh_command} '#{command_another}'"
495
392
  result = `#{full_cmd}`.strip
496
-
497
393
  unless result.empty?
498
394
  result.each_line do |line|
499
395
  if line =~ /vlan-id (\d+)/
@@ -507,94 +403,129 @@ module FreeRange
507
403
  end
508
404
  end
509
405
 
510
- # Виконуємо команду для отримання списку абонентів
511
- subscribers_result = `#{subscribers_command}`.strip
512
- if subscribers_result.empty?
513
- puts "Помилка: результат subscribers_command порожній. Перевір шлях або доступ."
514
- exit 1
515
- end
516
-
517
- # Обробка інтерфейсів
518
- if interface == "all"
519
- # Отримуємо список унікальних інтерфейсів
520
- command_interfaces = 'show configuration interfaces | no-more | display set | match dynamic-profile | match "ranges ([0-9]+(-[0-9]+)?)"'
521
- full_cmd = "#{ssh_command} '#{command_interfaces}'"
522
- result = `#{full_cmd}`.strip
406
+ # Основна логіка виконання
407
+ def self.run
408
+ options = {}
409
+ OptionParser.new do |opts|
410
+ opts.banner = "Використання: free-range <IP-адреса або hostname> [опції]"
411
+ opts.on("-u", "--username USERNAME", "Ім'я користувача для SSH") { |u| options[:username] = u }
412
+ opts.on("-p", "--password PASSWORD", "Пароль для SSH") { |p| options[:password] = p }
413
+ opts.on("-n", "--no-color", "Вимкнути кольоровий вивід") { options[:no_color] = true }
414
+ opts.on("-d", "--debug", "Увімкнути дебаг-режим") { options[:debug] = true }
415
+ opts.on("-t", "--table", "Вивести діаграму розподілу VLAN-ів") { options[:table] = true }
416
+ opts.on("-g", "--table-png PATH", "Зберегти діаграму розподілу VLAN-ів як PNG") { |path| options[:table_png] = path }
417
+ opts.on("-i", "--interface INTERFACE", "Назва інтерфейсу або 'all'") { |i| options[:interface] = i }
418
+ end.parse!
419
+
420
+ if ARGV.empty?
421
+ puts "Помилка: потрібно вказати IP-адресу або hostname роутера."
422
+ puts "Використання: free-range <IP-адреса або hostname> [-u|--username USERNAME] [-p|--password PASSWORD] [-n|--no-color] [-d|--debug] [-t|--table] [--table-png PATH] [-i|--interface INTERFACE]"
423
+ exit 1
424
+ end
523
425
 
524
- if result.empty?
525
- puts "Помилка: результат команди порожній. Перевір підключення або команду."
426
+ username = options[:username] || ENV['WHOAMI']
427
+ password = options[:password] || ENV['WHATISMYPASSWD']
428
+ if username.nil? || password.nil?
429
+ puts "Помилка: необхідно вказати ім'я користувача та пароль."
430
+ puts "Використовуйте опції -u|--username і -p|--password або змінні оточення WHOAMI і WHATISMYPASSWD."
526
431
  exit 1
527
432
  end
528
433
 
529
- interfaces = result.each_line.map { |line| line.split[2] }.uniq
530
- if interfaces.empty?
531
- puts "Помилка: не знайдено інтерфейсів із діапазонами."
434
+ use_color = !options[:no_color] && ENV['TERM'] && ENV['TERM'] != 'dumb'
435
+ debug = options[:debug]
436
+ table_mode = options[:table]
437
+ table_png_mode = options[:table_png]
438
+ interface = options[:interface]
439
+
440
+ # # Перевірка формату інтерфейсу
441
+ # if interface && interface != "all" && interface !~ /^[a-z]+-\d+\/\d+\/\d+$/
442
+ # puts "Помилка: некоректна назва інтерфейсу. Використовуйте формат 'xe-0/0/2' або 'all'."
443
+ # exit 1
444
+ # end
445
+
446
+ login = { target: ARGV[0], username: username, password: password }
447
+ target = ARGV[0].split('.')[0]
448
+ puts "Connecting to device: #{login[:target]}"
449
+
450
+ ssh_command = "sshpass -p \"#{login[:password]}\" ssh -C -x -4 -o StrictHostKeyChecking=no #{login[:username]}@#{login[:target]}"
451
+ subscribers_command = "ssh -C -x roffice /usr/local/share/noc/bin/radius-subscribers"
452
+
453
+ subscribers_result = `#{subscribers_command}`.strip
454
+ if subscribers_result.empty?
455
+ puts "Помилка: результат subscribers_command порожній. Перевір шлях або доступ."
532
456
  exit 1
533
457
  end
534
458
 
535
- interfaces.each do |intf|
459
+ if interface == "all"
460
+ command_interfaces = 'show configuration interfaces | no-more | display set | match dynamic-profile | match "ranges ([0-9]+(-[0-9]+)?)"'
461
+ full_cmd = "#{ssh_command} '#{command_interfaces}'"
462
+ result = `#{full_cmd}`.strip
463
+ if result.empty?
464
+ puts "Помилка: результат команди порожній. Перевір підключення або команду."
465
+ exit 1
466
+ end
467
+
468
+ interfaces = result.each_line.map { |line| line.split[2] }.uniq
469
+ if interfaces.empty?
470
+ puts "Помилка: не знайдено інтерфейсів із діапазонами."
471
+ exit 1
472
+ end
473
+
474
+ interfaces.each do |intf|
475
+ ranges = Ranges.new
476
+ vlans = Vlans.new
477
+ subscribers_result.each_line do |line|
478
+ if line.split.first =~ /dhcp(?:_[0-9a-fA-F.]+)?_([^:]+):(\d+)@#{Regexp.escape(target)}$/
479
+ subscriber_interface, vlan = $1, $2.to_i
480
+ vlans.add_vlan(vlan) if subscriber_interface == intf && vlan > 0
481
+ end
482
+ end
483
+
484
+ process_interface(ssh_command, intf, ranges)
485
+ if debug
486
+ puts "\nІнтерфейс: #{intf}"
487
+ Print.ranged(ranges)
488
+ Print.vlans(vlans)
489
+ Print.vlan_ranges(vlans)
490
+ puts
491
+ end
492
+ if table_png_mode
493
+ Print.table_png(ranges, vlans, table_png_mode, target, intf)
494
+ elsif table_mode
495
+ Print.table(ranges, vlans, use_color, target, intf)
496
+ else
497
+ Print.combined_ranges(ranges, vlans, use_color, target, intf)
498
+ end
499
+ end
500
+ else
536
501
  ranges = Ranges.new
537
502
  vlans = Vlans.new
538
-
539
- # Заповнюємо VLAN-и, фільтруючи за інтерфейсом
540
503
  subscribers_result.each_line do |line|
541
504
  if line.split.first =~ /dhcp(?:_[0-9a-fA-F.]+)?_([^:]+):(\d+)@#{Regexp.escape(target)}$/
542
505
  subscriber_interface, vlan = $1, $2.to_i
543
- vlans.add_vlan(vlan) if subscriber_interface == intf && vlan > 0
506
+ if interface
507
+ vlans.add_vlan(vlan) if subscriber_interface == interface && vlan > 0
508
+ else
509
+ vlans.add_vlan(vlan) if vlan > 0
510
+ end
544
511
  end
545
512
  end
546
513
 
547
- process_interface(ssh_command, intf, ranges)
548
-
549
- # Виводимо результати
514
+ process_interface(ssh_command, interface, ranges)
550
515
  if debug
551
- puts "\nІнтерфейс: #{intf}"
516
+ puts "\nІнтерфейс: #{interface}" if interface
552
517
  Print.ranged(ranges)
553
518
  Print.vlans(vlans)
554
519
  Print.vlan_ranges(vlans)
555
520
  puts
556
521
  end
557
522
  if table_png_mode
558
- Print.table_png(ranges, vlans, table_png_mode, target, intf)
523
+ Print.table_png(ranges, vlans, table_png_mode, target, interface)
559
524
  elsif table_mode
560
- Print.table(ranges, vlans, use_color, target, intf)
525
+ Print.table(ranges, vlans, use_color, target, interface)
561
526
  else
562
- Print.combined_ranges(ranges, vlans, use_color, target, intf)
527
+ Print.combined_ranges(ranges, vlans, use_color, target, interface)
563
528
  end
564
529
  end
565
- else
566
- ranges = Ranges.new
567
- vlans = Vlans.new
568
-
569
- # Заповнюємо VLAN-и, фільтруючи за інтерфейсом, якщо задано
570
- subscribers_result.each_line do |line|
571
- if line.split.first =~ /dhcp(?:_[0-9a-fA-F.]+)?_([^:]+):(\d+)@#{Regexp.escape(target)}$/
572
- subscriber_interface, vlan = $1, $2.to_i
573
- if interface
574
- vlans.add_vlan(vlan) if subscriber_interface == interface && vlan > 0
575
- else
576
- vlans.add_vlan(vlan) if vlan > 0
577
- end
578
- end
579
- end
580
-
581
- process_interface(ssh_command, interface, ranges)
582
-
583
- # Виводимо результати
584
- if debug
585
- puts "\nІнтерфейс: #{interface}" if interface
586
- Print.ranged(ranges)
587
- Print.vlans(vlans)
588
- Print.vlan_ranges(vlans)
589
- puts
590
- end
591
- if table_png_mode
592
- Print.table_png(ranges, vlans, table_png_mode, target, interface)
593
- elsif table_mode
594
- Print.table(ranges, vlans, use_color, target, interface)
595
- else
596
- Print.combined_ranges(ranges, vlans, use_color, target, interface)
597
- end
598
530
  end
599
-
600
531
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: free-range
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oleksandr Russkikh //aka Olden Gremlin
@@ -61,9 +61,8 @@ extensions: []
61
61
  extra_rdoc_files: []
62
62
  files:
63
63
  - bin/free-range
64
- - bin/free-range.~
65
64
  - lib/free-range.rb
66
- - lib/free-range.rb.~
65
+ - lib/free-range.rb~
67
66
  homepage: https://github.com/oldengremlin/free-range
68
67
  licenses:
69
68
  - Apache-2.0
data/bin/free-range.~ DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'free-range'
3
-
4
- # Виклик основної логіки, якщо потрібно (зазвичай код уже запускається в lib/free_range.rb)
5
-
6
- FreeRange.main if defined?(FreeRange.main)