free-range 0.2.1 → 0.2.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: ce3d41efcb7e6ee1906179227559f6d004b2d07816b3aa230756f4b64f6f0327
4
- data.tar.gz: f61a1feddd95066226602b0540c54efeada8a2846c6eb8ba297655a76240454d
3
+ metadata.gz: ad5fa03c9d62cb4dc177607da76525b1c63876a9a0bd62a334121d9ff2f5e3a9
4
+ data.tar.gz: 751d1afc032a061e359d038e18350a6f661acb01c465c5e0c0fb6c92720c98f6
5
5
  SHA512:
6
- metadata.gz: 2bb942063fff21ce9e3a80e1b6223986da7c2a6ff632d01eb05c2e9c2c1a00faaf6655cc544d5b74fcfe14e5dd64c933d581a016e78078c53d8308f98a1af488
7
- data.tar.gz: ce98a667f77b4ee2cf0dccca0cba7655b18ae2251e4e5284051bb366f725f58fc55c47ccaeb01522e056e2d68658804ffb6aee6d5a19d28d4bd23d235c8e56c3
6
+ metadata.gz: 9d4ff514571c47ea9f504eafb441536b19bd25b43fe23e786244651827629fc757b222364eb08542790a5c06c7150f8249525ac76b8acff2a9f8f15251eaaf74
7
+ data.tar.gz: 72de1f7f68fd17f8a22f64cd73c0d9cb89780246983859c5ed4281a22d4a237bf544564c1e64e1087ac0c19d88d331ef26fce433ea0b088c1d39a2c3e6b571f8
data/lib/free-range.rb CHANGED
@@ -2,7 +2,10 @@ require 'optparse'
2
2
  require 'rmagick'
3
3
  require 'fileutils'
4
4
 
5
- module FreeRange
5
+ class FreeRange
6
+ # Змінні екземпляра для статичних параметрів
7
+ attr_reader :config, :subscribers_result, :target, :use_color, :debug, :table_mode, :table_png_mode
8
+
6
9
  # Перевірка наявності ImageMagick
7
10
  begin
8
11
  require 'rmagick'
@@ -465,14 +468,12 @@ module FreeRange
465
468
  end
466
469
 
467
470
  # Метод для заповнення Ranges для одного інтерфейсу
468
- # @param config [Config] Configuration object with commands
469
471
  # @param interface [String, nil] Interface name or nil for all interfaces
470
472
  # @param ranges [Ranges] Object to store VLAN ranges
471
- # @param debug [Boolean] Enable debug output
472
473
  # @return [void]
473
- def self.process_interface(config, interface, ranges, debug = false)
474
- full_cmd = "#{config.ssh_command} '#{config.command_ranges(interface)}'"
475
- puts "[DEBUG] Executing command: #{full_cmd}" if debug
474
+ def process_interface(interface, ranges)
475
+ full_cmd = "#{@config.ssh_command} '#{@config.command_ranges(interface)}'"
476
+ puts "[DEBUG] Executing command: #{full_cmd}" if @debug
476
477
  result = `#{full_cmd}`.strip
477
478
  unless result.empty?
478
479
  result.each_line do |line|
@@ -484,8 +485,8 @@ module FreeRange
484
485
  end
485
486
  end
486
487
 
487
- full_cmd = "#{config.ssh_command} '#{config.command_demux(interface)}'"
488
- puts "[DEBUG] Executing command: #{full_cmd}" if debug
488
+ full_cmd = "#{@config.ssh_command} '#{@config.command_demux(interface)}'"
489
+ puts "[DEBUG] Executing command: #{full_cmd}" if @debug
489
490
  result = `#{full_cmd}`.strip
490
491
  unless result.empty?
491
492
  result.each_line do |line|
@@ -499,8 +500,8 @@ module FreeRange
499
500
  end
500
501
  end
501
502
 
502
- full_cmd = "#{config.ssh_command} '#{config.command_another(interface)}'"
503
- puts "[DEBUG] Executing command: #{full_cmd}" if debug
503
+ full_cmd = "#{@config.ssh_command} '#{@config.command_another(interface)}'"
504
+ puts "[DEBUG] Executing command: #{full_cmd}" if @debug
504
505
  result = `#{full_cmd}`.strip
505
506
  unless result.empty?
506
507
  result.each_line do |line|
@@ -516,20 +517,13 @@ module FreeRange
516
517
  end
517
518
 
518
519
  # Обробляє VLAN для інтерфейсу та виводить результати
519
- # @param config [Config] Configuration object with commands
520
520
  # @param interface [String, nil] Interface name or nil for all interfaces
521
- # @param subscribers_result [String] Result of subscribers command
522
- # @param target [String] Target device hostname
523
- # @param use_color [Boolean] Enable colored output
524
- # @param debug [Boolean] Enable debug output
525
- # @param table_mode [Boolean] Display VLAN distribution table
526
- # @param table_png_mode [String, nil] Path to save PNG or nil
527
521
  # @return [void]
528
- def self.process_and_output(config, interface, subscribers_result, target, use_color, debug, table_mode, table_png_mode)
522
+ def process_and_output(interface)
529
523
  ranges = Ranges.new
530
524
  vlans = Vlans.new
531
- subscribers_result.each_line do |line|
532
- if line.split.first =~ /dhcp(?:_[0-9a-fA-F.]+)?_([^:]+):(\d+)@#{Regexp.escape(target)}$/
525
+ @subscribers_result.each_line do |line|
526
+ if line.split.first =~ /dhcp(?:_[0-9a-fA-F.]+)?_([^:]+):(\d+)@#{Regexp.escape(@target)}$/
533
527
  subscriber_interface, vlan = $1, $2.to_i
534
528
  if interface
535
529
  vlans.add_vlan(vlan) if subscriber_interface == interface && vlan > 0
@@ -539,26 +533,33 @@ module FreeRange
539
533
  end
540
534
  end
541
535
 
542
- process_interface(config, interface, ranges, debug)
543
- if debug
536
+ process_interface(interface, ranges)
537
+ if @debug
544
538
  puts "\nІнтерфейс: #{interface}" if interface
545
539
  Print.ranged(ranges)
546
540
  Print.vlans(vlans)
547
541
  Print.vlan_ranges(vlans)
548
542
  puts
549
543
  end
550
- if table_png_mode
551
- Print.table_png(ranges, vlans, table_png_mode, target, interface)
552
- elsif table_mode
553
- Print.table(ranges, vlans, use_color, target, interface)
544
+ if @table_png_mode
545
+ Print.table_png(ranges, vlans, @table_png_mode, @target, interface)
546
+ elsif @table_mode
547
+ Print.table(ranges, vlans, @use_color, @target, interface)
554
548
  else
555
- Print.combined_ranges(ranges, vlans, use_color, target, interface)
549
+ Print.combined_ranges(ranges, vlans, @use_color, @target, interface)
556
550
  end
557
551
  end
558
552
 
559
- # Основна логіка виконання
560
- # @return [void]
561
- def self.run
553
+ # Ініціалізує об’єкт FreeRange із параметрами
554
+ def initialize
555
+ @config = nil
556
+ @subscribers_result = nil
557
+ @target = nil
558
+ @use_color = false
559
+ @debug = false
560
+ @table_mode = false
561
+ @table_png_mode = nil
562
+
562
563
  options = {}
563
564
  OptionParser.new do |opts|
564
565
  opts.banner = <<~BANNER
@@ -583,22 +584,22 @@ module FreeRange
583
584
  exit 1
584
585
  end
585
586
 
586
- # Визначаємо змінні з опцій
587
- use_color = !options[:no_color] && ENV['TERM'] && ENV['TERM'] != 'dumb'
588
- debug = options[:debug]
589
- table_mode = options[:table]
590
- table_png_mode = options[:table_png]
587
+ # Ініціалізуємо змінні екземпляра
588
+ @use_color = !options[:no_color] && ENV['TERM'] && ENV['TERM'] != 'dumb'
589
+ @debug = options[:debug]
590
+ @table_mode = options[:table]
591
+ @table_png_mode = options[:table_png]
591
592
  interface = options[:interface]
592
593
  config_file = options[:config_file]
593
594
 
594
595
  # Ініціалізуємо config з порожнім login
595
- config = Config.new({ target: ARGV[0], username: nil, password: nil })
596
+ @config = Config.new({ target: ARGV[0], username: nil, password: nil })
596
597
 
597
598
  # Завантажуємо конфігураційний файл, якщо він вказаний
598
599
  if config_file
599
600
  begin
600
601
  # Виконуємо конфігураційний файл у контексті існуючого об’єкта config
601
- config.instance_eval(File.read(config_file), config_file)
602
+ @config.instance_eval(File.read(config_file), config_file)
602
603
  rescue LoadError, Errno::ENOENT
603
604
  puts "Помилка: неможливо завантажити конфігураційний файл '#{config_file}'."
604
605
  exit 1
@@ -612,8 +613,8 @@ module FreeRange
612
613
  end
613
614
 
614
615
  # Визначаємо username і password з пріоритетом: аргументи > config > ENV
615
- username = options[:username] || config.username || ENV['WHOAMI']
616
- password = options[:password] || config.password || ENV['WHATISMYPASSWD']
616
+ username = options[:username] || @config.username || ENV['WHOAMI']
617
+ password = options[:password] || @config.password || ENV['WHATISMYPASSWD']
617
618
 
618
619
  if username.nil? || password.nil?
619
620
  puts "Помилка: необхідно вказати ім'я користувача та пароль."
@@ -622,41 +623,41 @@ module FreeRange
622
623
  end
623
624
 
624
625
  login = { target: ARGV[0], username: username, password: password }
625
- target = ARGV[0].split('.')[0]
626
+ @target = ARGV[0].split('.')[0]
626
627
  puts "Connecting to device: #{login[:target]}"
627
628
 
628
629
  # Оновлюємо config з актуальними login даними
629
- config = Config.new(login) { |c|
630
- c.username = config.username if config.username
631
- c.password = config.password if config.password
630
+ @config = Config.new(login) { |c|
631
+ c.username = @config.username if @config.username
632
+ c.password = @config.password if @config.password
632
633
  }
633
634
 
634
- if debug
635
+ if @debug
635
636
  puts "[DEBUG] Values:"
636
- puts "[DEBUG] use_color: #{use_color}"
637
- puts "[DEBUG] table_mode: #{table_mode}"
638
- puts "[DEBUG] table_png_mode: #{table_png_mode}"
637
+ puts "[DEBUG] use_color: #{@use_color}"
638
+ puts "[DEBUG] table_mode: #{@table_mode}"
639
+ puts "[DEBUG] table_png_mode: #{@table_png_mode}"
639
640
  puts "[DEBUG] interface: #{interface}"
640
641
  puts "[DEBUG] ARGV[0]: #{ARGV[0]}"
641
- puts "[DEBUG] target: #{target}"
642
+ puts "[DEBUG] target: #{@target}"
642
643
  puts "[DEBUG] login: #{login}"
643
644
  puts "[DEBUG] config_file: #{config_file}"
644
- puts "[DEBUG] config.username: #{config.username}"
645
- puts "[DEBUG] config.password: #{config.password}"
646
- puts "[DEBUG] config.ssh_command: #{config.ssh_command}"
647
- puts "[DEBUG] config.subscribers_command: #{config.subscribers_command}"
648
- puts "[DEBUG] config.command_interfaces: #{config.command_interfaces}"
645
+ puts "[DEBUG] config.username: #{@config.username}"
646
+ puts "[DEBUG] config.password: #{@config.password}"
647
+ puts "[DEBUG] config.ssh_command: #{@config.ssh_command}"
648
+ puts "[DEBUG] config.subscribers_command: #{@config.subscribers_command}"
649
+ puts "[DEBUG] config.command_interfaces: #{@config.command_interfaces}"
649
650
  end
650
651
 
651
- subscribers_result = `#{config.subscribers_command}`.strip
652
- if subscribers_result.empty?
652
+ @subscribers_result = `#{@config.subscribers_command}`.strip
653
+ if @subscribers_result.empty?
653
654
  puts "Помилка: результат subscribers_command порожній. Перевір шлях або доступ."
654
655
  exit 1
655
656
  end
656
657
 
657
658
  if interface == "all"
658
- full_cmd = "#{config.ssh_command} '#{config.command_interfaces}'"
659
- puts "[DEBUG] Executing command: #{full_cmd}" if debug
659
+ full_cmd = "#{@config.ssh_command} '#{@config.command_interfaces}'"
660
+ puts "[DEBUG] Executing command: #{full_cmd}" if @debug
660
661
  result = `#{full_cmd}`.strip
661
662
  if result.empty?
662
663
  puts "Помилка: результат команди порожній. Перевір підключення або команду."
@@ -670,10 +671,16 @@ module FreeRange
670
671
  end
671
672
 
672
673
  interfaces.each do |intf|
673
- process_and_output(config, intf, subscribers_result, target, use_color, debug, table_mode, table_png_mode)
674
+ process_and_output(intf)
674
675
  end
675
676
  else
676
- process_and_output(config, interface, subscribers_result, target, use_color, debug, table_mode, table_png_mode)
677
+ process_and_output(interface)
677
678
  end
678
679
  end
680
+
681
+ # Основна логіка виконання
682
+ # @return [void]
683
+ def self.run
684
+ new
685
+ end
679
686
  end
data/lib/free-range.rb.~ CHANGED
@@ -14,19 +14,30 @@ module FreeRange
14
14
 
15
15
  # Клас для зберігання конфігураційних команд
16
16
  class Config
17
- attr_accessor :ssh_command, :subscribers_command
17
+ attr_accessor :username, :password
18
18
 
19
- # Ініціалізує об’єкт конфігурації з значеннями за замовчуванням
19
+ # Ініціалізує об’єкт конфігурації
20
20
  # @param login [Hash] Hash with target, username, and password
21
21
  # @yield [self] Yields self for block-based configuration
22
22
  def initialize(login)
23
23
  @login = login
24
- # Значення за замовчуванням
25
- @ssh_command = "sshpass -p \"#{@login[:password]}\" ssh -C -x -4 -o StrictHostKeyChecking=no #{@login[:username]}@#{@login[:target]}"
26
- @subscribers_command = "ssh -C -x roffice /usr/local/share/noc/bin/radius-subscribers"
24
+ @username = nil
25
+ @password = nil
27
26
  yield self if block_given?
28
27
  end
29
28
 
29
+ # Повертає команду SSH
30
+ # @return [String] SSH command string
31
+ def ssh_command
32
+ "sshpass -p \"#{@login[:password]}\" ssh -C -x -4 -o StrictHostKeyChecking=no #{@login[:username]}@#{@login[:target]}"
33
+ end
34
+
35
+ # Повертає команду для отримання даних про передплатників
36
+ # @return [String] Subscribers command string
37
+ def subscribers_command
38
+ "ssh -C -x roffice /usr/local/share/noc/bin/radius-subscribers"
39
+ end
40
+
30
41
  # Повертає команду для отримання списку інтерфейсів
31
42
  # @return [String] Command to fetch interfaces
32
43
  def command_interfaces
@@ -36,21 +47,21 @@ module FreeRange
36
47
  # Повертає команду для отримання діапазонів VLAN
37
48
  # @param interface [String, nil] Interface name or nil for all interfaces
38
49
  # @return [String] Command to fetch VLAN ranges
39
- def command_ranges(interface)
50
+ def command_ranges(interface = nil)
40
51
  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]+)?)"'
41
52
  end
42
53
 
43
54
  # Повертає команду для отримання демультиплексорних VLAN
44
55
  # @param interface [String, nil] Interface name or nil for all interfaces
45
56
  # @return [String] Command to fetch demux VLANs
46
- def command_demux(interface)
57
+ def command_demux(interface = nil)
47
58
  interface ? "show configuration interfaces #{interface} | display set | match unnumbered-address" : 'show configuration interfaces | display set | match unnumbered-address'
48
59
  end
49
60
 
50
61
  # Повертає команду для отримання інших VLAN
51
62
  # @param interface [String, nil] Interface name or nil for all interfaces
52
63
  # @return [String] Command to fetch other VLANs
53
- def command_another(interface)
64
+ def command_another(interface = nil)
54
65
  interface ? "show configuration interfaces #{interface} | display set | match vlan-id" : 'show configuration interfaces | display set | match vlan-id'
55
66
  end
56
67
  end
@@ -457,9 +468,11 @@ module FreeRange
457
468
  # @param config [Config] Configuration object with commands
458
469
  # @param interface [String, nil] Interface name or nil for all interfaces
459
470
  # @param ranges [Ranges] Object to store VLAN ranges
471
+ # @param debug [Boolean] Enable debug output
460
472
  # @return [void]
461
- def self.process_interface(config, interface, ranges)
473
+ def self.process_interface(config, interface, ranges, debug = false)
462
474
  full_cmd = "#{config.ssh_command} '#{config.command_ranges(interface)}'"
475
+ puts "[DEBUG] Executing command: #{full_cmd}" if debug
463
476
  result = `#{full_cmd}`.strip
464
477
  unless result.empty?
465
478
  result.each_line do |line|
@@ -472,6 +485,7 @@ module FreeRange
472
485
  end
473
486
 
474
487
  full_cmd = "#{config.ssh_command} '#{config.command_demux(interface)}'"
488
+ puts "[DEBUG] Executing command: #{full_cmd}" if debug
475
489
  result = `#{full_cmd}`.strip
476
490
  unless result.empty?
477
491
  result.each_line do |line|
@@ -486,6 +500,7 @@ module FreeRange
486
500
  end
487
501
 
488
502
  full_cmd = "#{config.ssh_command} '#{config.command_another(interface)}'"
503
+ puts "[DEBUG] Executing command: #{full_cmd}" if debug
489
504
  result = `#{full_cmd}`.strip
490
505
  unless result.empty?
491
506
  result.each_line do |line|
@@ -500,6 +515,47 @@ module FreeRange
500
515
  end
501
516
  end
502
517
 
518
+ # Обробляє VLAN для інтерфейсу та виводить результати
519
+ # @param config [Config] Configuration object with commands
520
+ # @param interface [String, nil] Interface name or nil for all interfaces
521
+ # @param subscribers_result [String] Result of subscribers command
522
+ # @param target [String] Target device hostname
523
+ # @param use_color [Boolean] Enable colored output
524
+ # @param debug [Boolean] Enable debug output
525
+ # @param table_mode [Boolean] Display VLAN distribution table
526
+ # @param table_png_mode [String, nil] Path to save PNG or nil
527
+ # @return [void]
528
+ def self.process_and_output(config, interface, subscribers_result, target, use_color, debug, table_mode, table_png_mode)
529
+ ranges = Ranges.new
530
+ vlans = Vlans.new
531
+ subscribers_result.each_line do |line|
532
+ if line.split.first =~ /dhcp(?:_[0-9a-fA-F.]+)?_([^:]+):(\d+)@#{Regexp.escape(target)}$/
533
+ subscriber_interface, vlan = $1, $2.to_i
534
+ if interface
535
+ vlans.add_vlan(vlan) if subscriber_interface == interface && vlan > 0
536
+ else
537
+ vlans.add_vlan(vlan) if vlan > 0
538
+ end
539
+ end
540
+ end
541
+
542
+ process_interface(config, interface, ranges, debug)
543
+ if debug
544
+ puts "\nІнтерфейс: #{interface}" if interface
545
+ Print.ranged(ranges)
546
+ Print.vlans(vlans)
547
+ Print.vlan_ranges(vlans)
548
+ puts
549
+ end
550
+ if table_png_mode
551
+ Print.table_png(ranges, vlans, table_png_mode, target, interface)
552
+ elsif table_mode
553
+ Print.table(ranges, vlans, use_color, target, interface)
554
+ else
555
+ Print.combined_ranges(ranges, vlans, use_color, target, interface)
556
+ end
557
+ end
558
+
503
559
  # Основна логіка виконання
504
560
  # @return [void]
505
561
  def self.run
@@ -527,14 +583,7 @@ module FreeRange
527
583
  exit 1
528
584
  end
529
585
 
530
- username = options[:username] || ENV['WHOAMI']
531
- password = options[:password] || ENV['WHATISMYPASSWD']
532
- if username.nil? || password.nil?
533
- puts "Помилка: необхідно вказати ім'я користувача та пароль."
534
- puts "Використовуйте опції -u/--username і -p/--password або змінні оточення WHOAMI і WHATISMYPASSWD."
535
- exit 1
536
- end
537
-
586
+ # Визначаємо змінні з опцій
538
587
  use_color = !options[:no_color] && ENV['TERM'] && ENV['TERM'] != 'dumb'
539
588
  debug = options[:debug]
540
589
  table_mode = options[:table]
@@ -542,25 +591,63 @@ module FreeRange
542
591
  interface = options[:interface]
543
592
  config_file = options[:config_file]
544
593
 
545
- login = { target: ARGV[0], username: username, password: password }
546
- target = ARGV[0].split('.')[0]
547
- puts "Connecting to device: #{login[:target]}"
594
+ # Ініціалізуємо config з порожнім login
595
+ config = Config.new({ target: ARGV[0], username: nil, password: nil })
548
596
 
549
- # Ініціалізація конфігурації
550
- config = Config.new(login)
597
+ # Завантажуємо конфігураційний файл, якщо він вказаний
551
598
  if config_file
552
599
  begin
553
- load config_file
554
- config = FreeRange::Config.new(login) { |c| eval(File.read(config_file), binding, config_file) }
600
+ # Виконуємо конфігураційний файл у контексті існуючого об’єкта config
601
+ config.instance_eval(File.read(config_file), config_file)
555
602
  rescue LoadError, Errno::ENOENT
556
603
  puts "Помилка: неможливо завантажити конфігураційний файл '#{config_file}'."
557
604
  exit 1
605
+ rescue ArgumentError => e
606
+ puts "Помилка в аргументах конфігураційного файлу '#{config_file}': #{e.message}"
607
+ exit 1
558
608
  rescue StandardError => e
559
609
  puts "Помилка в конфігураційному файлі '#{config_file}': #{e.message}"
560
610
  exit 1
561
611
  end
562
612
  end
563
613
 
614
+ # Визначаємо username і password з пріоритетом: аргументи > config > ENV
615
+ username = options[:username] || config.username || ENV['WHOAMI']
616
+ password = options[:password] || config.password || ENV['WHATISMYPASSWD']
617
+
618
+ if username.nil? || password.nil?
619
+ puts "Помилка: необхідно вказати ім'я користувача та пароль."
620
+ puts "Використовуйте опції -u/--username і -p/--password, конфігураційний файл або змінні оточення WHOAMI і WHATISMYPASSWD."
621
+ exit 1
622
+ end
623
+
624
+ login = { target: ARGV[0], username: username, password: password }
625
+ target = ARGV[0].split('.')[0]
626
+ puts "Connecting to device: #{login[:target]}"
627
+
628
+ # Оновлюємо config з актуальними login даними
629
+ config = Config.new(login) { |c|
630
+ c.username = config.username if config.username
631
+ c.password = config.password if config.password
632
+ }
633
+
634
+ if debug
635
+ puts "[DEBUG] Values:"
636
+ puts "[DEBUG] use_color: #{use_color}"
637
+ puts "[DEBUG] table_mode: #{table_mode}"
638
+ puts "[DEBUG] table_png_mode: #{table_png_mode}"
639
+ puts "[DEBUG] interface: #{interface}"
640
+ puts "[DEBUG] ARGV[0]: #{ARGV[0]}"
641
+ puts "[DEBUG] target: #{target}"
642
+ puts "[DEBUG] login: #{login}"
643
+ puts "[DEBUG] config_file: #{config_file}"
644
+ puts "[DEBUG] config.username: #{config.username}"
645
+ puts "[DEBUG] config.password: #{config.password}"
646
+ puts "[DEBUG] config.ssh_command: #{config.ssh_command}"
647
+ puts "[DEBUG] config.subscribers_command: #{config.subscribers_command}"
648
+ puts "[DEBUG] config.command_interfaces: #{config.command_interfaces}"
649
+ end
650
+
564
651
  subscribers_result = `#{config.subscribers_command}`.strip
565
652
  if subscribers_result.empty?
566
653
  puts "Помилка: результат subscribers_command порожній. Перевір шлях або доступ."
@@ -569,6 +656,7 @@ module FreeRange
569
656
 
570
657
  if interface == "all"
571
658
  full_cmd = "#{config.ssh_command} '#{config.command_interfaces}'"
659
+ puts "[DEBUG] Executing command: #{full_cmd}" if debug
572
660
  result = `#{full_cmd}`.strip
573
661
  if result.empty?
574
662
  puts "Помилка: результат команди порожній. Перевір підключення або команду."
@@ -582,60 +670,10 @@ module FreeRange
582
670
  end
583
671
 
584
672
  interfaces.each do |intf|
585
- ranges = Ranges.new
586
- vlans = Vlans.new
587
- subscribers_result.each_line do |line|
588
- if line.split.first =~ /dhcp(?:_[0-9a-fA-F.]+)?_([^:]+):(\d+)@#{Regexp.escape(target)}$/
589
- subscriber_interface, vlan = $1, $2.to_i
590
- vlans.add_vlan(vlan) if subscriber_interface == intf && vlan > 0
591
- end
592
- end
593
-
594
- process_interface(config, intf, ranges)
595
- if debug
596
- puts "\nІнтерфейс: #{intf}"
597
- Print.ranged(ranges)
598
- Print.vlans(vlans)
599
- Print.vlan_ranges(vlans)
600
- puts
601
- end
602
- if table_png_mode
603
- Print.table_png(ranges, vlans, table_png_mode, target, intf)
604
- elsif table_mode
605
- Print.table(ranges, vlans, use_color, target, intf)
606
- else
607
- Print.combined_ranges(ranges, vlans, use_color, target, intf)
608
- end
673
+ process_and_output(config, intf, subscribers_result, target, use_color, debug, table_mode, table_png_mode)
609
674
  end
610
675
  else
611
- ranges = Ranges.new
612
- vlans = Vlans.new
613
- subscribers_result.each_line do |line|
614
- if line.split.first =~ /dhcp(?:_[0-9a-fA-F.]+)?_([^:]+):(\d+)@#{Regexp.escape(target)}$/
615
- subscriber_interface, vlan = $1, $2.to_i
616
- if interface
617
- vlans.add_vlan(vlan) if subscriber_interface == interface && vlan > 0
618
- else
619
- vlans.add_vlan(vlan) if vlan > 0
620
- end
621
- end
622
- end
623
-
624
- process_interface(config, interface, ranges)
625
- if debug
626
- puts "\nІнтерфейс: #{interface}" if interface
627
- Print.ranged(ranges)
628
- Print.vlans(vlans)
629
- Print.vlan_ranges(vlans)
630
- puts
631
- end
632
- if table_png_mode
633
- Print.table_png(ranges, vlans, table_png_mode, target, interface)
634
- elsif table_mode
635
- Print.table(ranges, vlans, use_color, target, interface)
636
- else
637
- Print.combined_ranges(ranges, vlans, use_color, target, interface)
638
- end
676
+ process_and_output(config, interface, subscribers_result, target, use_color, debug, table_mode, table_png_mode)
639
677
  end
640
678
  end
641
679
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: free-range
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oleksandr Russkikh //aka Olden Gremlin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-09-29 00:00:00.000000000 Z
11
+ date: 2025-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rmagick
@@ -77,7 +77,6 @@ files:
77
77
  - bin/free-range
78
78
  - lib/free-range.rb
79
79
  - lib/free-range.rb.~
80
- - lib/free-range.rb~
81
80
  homepage: https://github.com/oldengremlin/free-range
82
81
  licenses:
83
82
  - Apache-2.0
data/lib/free-range.rb~ DELETED
@@ -1,531 +0,0 @@
1
- require 'optparse'
2
- require 'rmagick'
3
- require 'fileutils'
4
-
5
- module FreeRange
6
-
7
- # Перевірка наявності ImageMagick
8
- begin
9
- require 'rmagick'
10
- rescue LoadError
11
- puts "Помилка: бібліотека rmagick не встановлена або ImageMagick недоступний."
12
- puts "Встановіть ImageMagick і виконайте: gem install rmagick"
13
- exit 1
14
- end
15
-
16
- # Абстрактний клас для роботи з VLAN
17
- class VlanContainer
18
- def initialize
19
- @vlans = []
20
- end
21
-
22
- # Додаємо VLAN до списку (віртуальний метод, може бути перевизначений)
23
- def add_vlan(vlan)
24
- @vlans << vlan
25
- end
26
-
27
- # Повертаємо масив VLAN
28
- def vlans
29
- @vlans.uniq.sort
30
- end
31
-
32
- # Створюємо хеш діапазонів із VLAN
33
- def ranges
34
- return {} if @vlans.empty?
35
-
36
- vlan_ranges_hash = {}
37
- sorted_vlans = vlans
38
- start = sorted_vlans.first
39
- prev = start
40
-
41
- sorted_vlans[1..-1].each do |vlan|
42
- unless vlan == prev + 1
43
- vlan_ranges_hash[start] = prev
44
- start = vlan
45
- end
46
- prev = vlan
47
- end
48
- vlan_ranges_hash[start] = prev
49
-
50
- vlan_ranges_hash
51
- end
52
- end
53
-
54
- # Клас для зберігання та обробки діапазонів
55
- class Ranges < VlanContainer
56
- def initialize
57
- super
58
- @another_in_ranges = []
59
- end
60
-
61
- # Додаємо діапазон до списку VLAN-ів
62
- def add_range(start, finish)
63
- (start..finish).each { |vlan| @vlans << vlan }
64
- end
65
-
66
- # Додаємо діапазон до списку "інших" VLAN-ів
67
- def add_another_range(start, finish)
68
- (start..finish).each { |vlan| @another_in_ranges << vlan }
69
- end
70
-
71
- # Повертаємо масив "інших" VLAN-ів
72
- def another_in_ranges
73
- @another_in_ranges.uniq.sort
74
- end
75
- end
76
-
77
- # Клас для зберігання та обробки VLAN-ів
78
- class Vlans < VlanContainer
79
- # Метод add_vlan уже успадковано від VlanContainer
80
- # Метод vlans уже успадковано
81
- # Метод ranges уже успадковано, але перейменуємо для зрозумілості
82
- alias vlan_ranges ranges
83
- end
84
-
85
- # Клас для виводу даних
86
- class Print
87
- def self.ranged(ranges)
88
- puts "\nСформований хеш діапазонів (в порядку зростання):"
89
- if ranges.ranges.empty?
90
- puts "Не знайдено діапазонів."
91
- else
92
- ranges.ranges.sort_by { |k, _| k }.each do |start, end_val|
93
- puts "range[#{start}]=#{end_val}"
94
- end
95
- end
96
- end
97
-
98
- def self.vlans(vlans)
99
- puts "\nЗайняті VLAN-и в межах діапазонів (в порядку зростання):"
100
- if vlans.vlans.empty?
101
- puts "Не знайдено VLAN-ів у межах діапазонів."
102
- else
103
- puts vlans.vlans.uniq.sort.join(", ")
104
- end
105
- end
106
-
107
- def self.vlan_ranges(vlans)
108
- puts "\nДіапазони VLAN-ів (в порядку зростання):"
109
- vlan_ranges = vlans.vlan_ranges
110
- if vlan_ranges.empty?
111
- puts "Не знайдено діапазонів VLAN-ів."
112
- else
113
- vlan_ranges.sort_by { |k, _| k }.each do |start, end_val|
114
- puts "range[#{start}]=#{end_val}"
115
- end
116
- end
117
- end
118
-
119
- def self.combined_ranges(ranges, vlans, use_color, target, interface = nil)
120
- if ranges.ranges.empty?
121
- puts "Не знайдено діапазонів."
122
- return
123
- end
124
-
125
- all_vlans, _status_counts = build_vlan_statuses(ranges, vlans)
126
- result = []
127
- sorted_vlans = all_vlans.keys.sort
128
- start = sorted_vlans.first
129
- prev = start
130
- status = all_vlans[start]
131
-
132
- sorted_vlans[1..-1].each do |vlan|
133
- unless vlan == prev + 1 && all_vlans[vlan] == status
134
- result << format_range(start, prev, status, use_color)
135
- start = vlan
136
- status = all_vlans[vlan]
137
- end
138
- prev = vlan
139
- end
140
- result << format_range(start, prev, status, use_color)
141
-
142
- puts result.join(',')
143
- end
144
-
145
- def self.table(ranges, vlans, use_color, target, interface = nil)
146
- puts "VLAN Distribution for #{target}#{interface ? " (#{interface})" : ''}"
147
- all_vlans, status_counts = build_vlan_statuses(ranges, vlans)
148
-
149
- puts " 0 1 2 3 4 5 6 7 8 9 "
150
- (0..40).each do |h|
151
- start_vlan = h * 100
152
- end_vlan = [start_vlan + 99, 4094].min
153
- row = (start_vlan..end_vlan).map { |vlan| format_table_char(all_vlans[vlan] || ' ', use_color) }.join
154
- puts "#{format("%4d", start_vlan)} #{row}"
155
- end
156
-
157
- legend_parts = [
158
- ["Legend: ", nil],
159
- ["f", 'f'], ["=free", nil], [", ", nil],
160
- ["b", 'b'], ["=busy", nil], [", ", nil],
161
- ["e", 'e'], ["=error", nil], [", ", nil],
162
- ["c", 'c'], ["=configured", nil], [", ", nil],
163
- ["a", 'a'], ["=another", nil], [", ", nil],
164
- ["u", 'u'], ["=unused", nil]
165
- ]
166
- legend_text = legend_parts.map do |text, status|
167
- if status && use_color
168
- format_table_char(status, use_color)
169
- else
170
- text
171
- end
172
- end.join
173
- puts "\n#{legend_text}"
174
-
175
- summary_parts = [
176
- ["Total: ", nil],
177
- ["f", 'f'], ["=#{status_counts['f']}", nil], [", ", nil],
178
- ["b", 'b'], ["=#{status_counts['b']}", nil], [", ", nil],
179
- ["e", 'e'], ["=#{status_counts['e']}", nil], [", ", nil],
180
- ["c", 'c'], ["=#{status_counts['c']}", nil], [", ", nil],
181
- ["a", 'a'], ["=#{status_counts['a']}", nil], [", ", nil],
182
- ["u", 'u'], ["=#{status_counts['u']}", nil]
183
- ]
184
- summary_text = summary_parts.map do |text, status|
185
- if status && use_color
186
- format_table_char(status, use_color)
187
- else
188
- text
189
- end
190
- end.join
191
- puts summary_text
192
- end
193
-
194
- def self.table_png(ranges, vlans, path, target, interface = nil)
195
- all_vlans, status_counts = build_vlan_statuses(ranges, vlans)
196
- cell_width = 12
197
- cell_height = 20
198
- rows = 41
199
- cols = 100
200
- header_height = 60
201
- label_width = 50
202
- width = label_width + cols * cell_width + 10
203
- height = header_height + rows * cell_height + 20 + 50
204
- font_size = 14
205
- title_font_size = 18
206
- font = 'Courier'
207
-
208
- canvas = Magick::Image.new(width, height) { |options| options.background_color = 'white' }
209
- gc = Magick::Draw.new
210
- gc.font = font
211
- gc.pointsize = font_size
212
- gc.text_antialias = true
213
-
214
- gc.fill('black')
215
- gc.pointsize = title_font_size
216
- gc.text(10, 25, "VLAN Distribution for #{target}#{interface ? " (#{interface})" : ''}")
217
- gc.pointsize = font_size
218
-
219
- (0..9).each do |i|
220
- x = label_width + i * 10 * cell_width - 3
221
- gc.fill('black')
222
- gc.text(x + 5, header_height - 5, i.to_s)
223
- end
224
-
225
- (0..40).each do |h|
226
- start_vlan = h * 100
227
- end_vlan = [start_vlan + 99, 4094].min
228
- y = header_height + h * cell_height
229
- gc.fill('black')
230
- gc.text(5, y + font_size, format("%4d", start_vlan))
231
-
232
- (start_vlan..end_vlan).each_with_index do |vlan, i|
233
- status = all_vlans[vlan] || ' '
234
- x = label_width + i * cell_width
235
- color = case status
236
- when 'f' then '#00FF00' # Зелений
237
- when 'b' then '#FFFF00' # Жовтий
238
- when 'e' then '#FF0000' # Червоний
239
- when 'c' then '#FF00FF' # Фіолетовий
240
- when 'a' then '#0000FF' # Синій
241
- when 'u' then '#555555' # Темно-сірий
242
- else 'white' # Пробіл
243
- end
244
- gc.fill(color)
245
- gc.rectangle(x, y, x + cell_width - 1, y + cell_height - 1)
246
- gc.fill('black')
247
- gc.text(x + 2, y + font_size, status) unless status == ' '
248
- end
249
- end
250
-
251
- legend_y = height - 50
252
- x = 10
253
- legend_parts = [
254
- ["Legend: ", nil],
255
- ["f", '#00FF00'], ["=free", nil], [", ", nil],
256
- ["b", '#FFFF00'], ["=busy", nil], [", ", nil],
257
- ["e", '#FF0000'], ["=error", nil], [", ", nil],
258
- ["c", '#FF00FF'], ["=configured", nil], [", ", nil],
259
- ["a", '#0000FF'], ["=another", nil], [", ", nil],
260
- ["u", '#555555'], ["=unused", nil]
261
- ]
262
- legend_parts.each do |text, color|
263
- if color
264
- gc.fill(color)
265
- gc.rectangle(x, legend_y - font_size + 2, x + 10, legend_y + 2)
266
- gc.fill('black')
267
- gc.text(x + 2, legend_y, text)
268
- x += 12
269
- else
270
- gc.fill('black')
271
- gc.text(x, legend_y, text)
272
- x += text.length * 8
273
- end
274
- end
275
-
276
- summary_y = height - 30
277
- x = 10
278
- summary_parts = [
279
- ["Total: ", nil],
280
- ["f", '#00FF00'], ["=#{status_counts['f']}", nil], [", ", nil],
281
- ["b", '#FFFF00'], ["=#{status_counts['b']}", nil], [", ", nil],
282
- ["e", '#FF0000'], ["=#{status_counts['e']}", nil], [", ", nil],
283
- ["c", '#FF00FF'], ["=#{status_counts['c']}", nil], [", ", nil],
284
- ["a", '#0000FF'], ["=#{status_counts['a']}", nil], [", ", nil],
285
- ["u", '#555555'], ["=#{status_counts['u']}", nil]
286
- ]
287
- summary_parts.each do |text, color|
288
- if color
289
- gc.fill(color)
290
- gc.rectangle(x, summary_y - font_size + 2, x + 10, summary_y + 2)
291
- gc.fill('black')
292
- gc.text(x + 2, summary_y, text)
293
- x += 12
294
- else
295
- gc.fill('black')
296
- gc.text(x, summary_y, text)
297
- x += text.length * 8
298
- end
299
- end
300
-
301
- gc.draw(canvas)
302
- FileUtils.mkdir_p(path) unless Dir.exist?(path)
303
- filename = File.join(path, "free-range-#{target}#{interface ? "-#{interface.tr('/', '-')}" : ''}.png")
304
- canvas.write(filename)
305
- puts "Зображення збережено: #{filename}"
306
- end
307
-
308
- private
309
-
310
- def self.build_vlan_statuses(ranges, vlans)
311
- all_vlans = {}
312
- ranges.ranges.each do |start, finish|
313
- (start..finish).each { |vlan| all_vlans[vlan] = 'f' }
314
- end
315
- vlans.vlans.uniq.each { |vlan| all_vlans[vlan] = all_vlans.key?(vlan) ? 'b' : 'e' }
316
- (1..4094).each { |vlan| all_vlans[vlan] = 'u' unless all_vlans.key?(vlan) }
317
- ranges.another_in_ranges.each { |vlan| all_vlans[vlan] = all_vlans.key?(vlan) && all_vlans[vlan] != 'u' ? 'c' : 'a' }
318
- status_counts = { 'f' => 0, 'b' => 0, 'e' => 0, 'c' => 0, 'a' => 0, 'u' => 0 }
319
- all_vlans.each_value { |status| status_counts[status] += 1 }
320
-
321
- [all_vlans, status_counts]
322
- end
323
-
324
- def self.format_range(start, finish, status, use_color)
325
- range_text = start == finish ? "#{start}" : "#{start}-#{finish}"
326
- range_text_with_status = "#{range_text}(#{status})"
327
- if use_color
328
- case status
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 # Без кольору для інших статусів
336
- end
337
- else
338
- range_text_with_status # Текстовий вивід зі статусами
339
- end
340
- end
341
-
342
- def self.format_table_char(status, use_color)
343
- if use_color
344
- case 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 # Без кольору для інших статусів
352
- end
353
- else
354
- status # Текстовий вивід без кольорів
355
- end
356
- end
357
- end
358
-
359
- # Метод для заповнення Ranges для одного інтерфейсу
360
- def self.process_interface(ssh_command, interface, ranges)
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]+)?)"'
362
- command_demux = interface ? "show configuration interfaces #{interface} | display set | match unnumbered-address" : 'show configuration interfaces | display set | match unnumbered-address'
363
- command_another = interface ? "show configuration interfaces #{interface} | display set | match vlan-id" : 'show configuration interfaces | display set | match vlan-id'
364
-
365
- full_cmd = "#{ssh_command} '#{command_ranges}'"
366
- result = `#{full_cmd}`.strip
367
- unless result.empty?
368
- result.each_line do |line|
369
- if line =~ /ranges (\d+)(?:-(\d+))?/
370
- start_range = $1.to_i
371
- end_range = $2 ? $2.to_i : $1.to_i
372
- ranges.add_range(start_range, end_range)
373
- end
374
- end
375
- end
376
-
377
- full_cmd = "#{ssh_command} '#{command_demux}'"
378
- result = `#{full_cmd}`.strip
379
- unless result.empty?
380
- result.each_line do |line|
381
- if line =~ /unit (\d+)/
382
- start_range = $1.to_i
383
- if start_range > 0
384
- end_range = start_range
385
- ranges.add_range(start_range, end_range)
386
- end
387
- end
388
- end
389
- end
390
-
391
- full_cmd = "#{ssh_command} '#{command_another}'"
392
- result = `#{full_cmd}`.strip
393
- unless result.empty?
394
- result.each_line do |line|
395
- if line =~ /vlan-id (\d+)/
396
- start_range = $1.to_i
397
- if start_range > 0
398
- end_range = start_range
399
- ranges.add_another_range(start_range, end_range)
400
- end
401
- end
402
- end
403
- end
404
- end
405
-
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
425
-
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."
431
- exit 1
432
- end
433
-
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 порожній. Перевір шлях або доступ."
456
- exit 1
457
- end
458
-
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
501
- ranges = Ranges.new
502
- vlans = Vlans.new
503
- subscribers_result.each_line do |line|
504
- if line.split.first =~ /dhcp(?:_[0-9a-fA-F.]+)?_([^:]+):(\d+)@#{Regexp.escape(target)}$/
505
- subscriber_interface, vlan = $1, $2.to_i
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
511
- end
512
- end
513
-
514
- process_interface(ssh_command, interface, ranges)
515
- if debug
516
- puts "\nІнтерфейс: #{interface}" if interface
517
- Print.ranged(ranges)
518
- Print.vlans(vlans)
519
- Print.vlan_ranges(vlans)
520
- puts
521
- end
522
- if table_png_mode
523
- Print.table_png(ranges, vlans, table_png_mode, target, interface)
524
- elsif table_mode
525
- Print.table(ranges, vlans, use_color, target, interface)
526
- else
527
- Print.combined_ranges(ranges, vlans, use_color, target, interface)
528
- end
529
- end
530
- end
531
- end