#!/usr/bin/env perl

use 5.012;
use warnings;
no warnings 'uninitialized';
use Data::Dumper;

use Carp;
use Encode;
use Encode::CN;
use YAML;
use YAML::Dumper;

use File::Slurp;
use Config::Tiny;
use Cwd qw'cwd abs_path';
use File::Basename;
use Huawei::Info::Ipaddr;
use POSIX qw/strftime/;
use Spreadsheet::WriteExcel;

#初始化本地时间
my $time = strftime( "%Y%m%d", localtime() );

#获取当前脚本绝对路径
my $dir = dirname( abs_path($0) );

#指定系统路径分隔符
my $split = "/";

#初始化相关变量
my ( $config_dir, $report_dir );

#设置配置文件路径
my $config_ini = $dir . $split . "config.ini";

#------------------------------------------------------------------------------
#机房数据映射哈希
#------------------------------------------------------------------------------
my %location = (
  "bbbsuan"    => "xxx房",
  "bbbboshi"   => "xxx机房",
  "bbbian"     => "xxx房",
  "bbbyangmen" => "xxx机房",
  "bbb0"       => "xxx机房",
  "bbbXDS"     => "xxx厦",
  "bbbCDS"     => "xxx厦",
  "bbbHXHL"    => "xxx鸿联",
  "bbbHXDX"    => "xxx电信",
  "bbbHXLD"    => "xxx联通",
);

#------------------------------------------------------------------------------
#初始化配置文件路径
#------------------------------------------------------------------------------
sub create_ini {

  #配置文件初始化
  my $text = '#配置目录文件夹
config_dir = config
report_dir = report
';

  #将配置文件转码未CP936
  $text = decode( 'cp936', "$text" );

  #判断INI配置文件是否存在
  write_file( $config_ini, $text );
}

#------------------------------------------------------------------------------
#防火墙配置解析准备
#------------------------------------------------------------------------------
sub prepare {

  #初始化配置文件
  create_ini() unless -f $config_ini;
  my $config = new Config::Tiny->read($config_ini);

  #如果实例化成功则croak
  croak "请检查配置文件是否存在" unless $config;

  #网络配置文件夹
  $config_dir = $dir . $split . $config->{"_"}{"config_dir"};

  #报表输出文件夹
  $report_dir = $dir . $split . $config->{"_"}{"report_dir"};

  #检查相关文件夹是否存在,不存在则新建
  mkdir $config_dir unless -d $config_dir;
  mkdir $report_dir unless -d $report_dir;
}

#------------------------------------------------------------------------------
#将数据写入EXCEL表格
#------------------------------------------------------------------------------
sub write_excel {

  #接收外部传递的配置文件夹
  my $rev = shift;

  #获取计算结果
  my $ret = ip_info($rev);

  #网络设备本身IP信息
  my $ret1 = $ret->[0];

  #接入层交换机MAC信息
  my $ret2 = $ret->[1];

  #Incomplete ARP MAC信息
  my $ret3 = $ret->[2];

  #IPPHONE MAC 信息
  my $ret4 = $ret->[3];

  # 802.1x 认证信息
  my $ret5 = $ret->[4];

  #异常的ARP MAC信息
  my $ret6 = $ret->[5];

  #获取解析到的IP并打印
  my $ret1_count = scalar( keys %{$ret1} );
  my $ret2_count = scalar( keys %{$ret2} );
  my $ret3_count = scalar( keys %{$ret3} );
  my $ret4_count = scalar( keys %{$ret4} );
  my $ret5_count = scalar( keys %{$ret5} );
  my $ret6_count = scalar( keys %{$ret6} );

  #打印扫描到的IP数量
  say "共扫描到 $ret1_count 个网络设备IP";
  say "共扫描到 $ret2_count 个服务器IP";
  say "共扫描到 $ret3_count 个Incomplete IP";
  say "共扫描到 $ret4_count 台IPPHONE_模拟话机";
  say "共扫描到 $ret5_count 台 dot1x 认证PC主机";
  say "共有 $ret6_count 个解析异常IP \n";

  #创建EXCEL表格
  my $wb_name = "IP地址归属表" . "_$time" . '.xls';

  #设备绝对路径
  my $filename = $report_dir . $split . $wb_name;
  my $workbook = Spreadsheet::WriteExcel->new($filename);

  #新建sheet
  my $sheet1 = decode( 'cp936', '网络设备 IP 定位表' );
  my $sheet2 = decode( 'cp936', '服务器 IP 定位表' );
  my $sheet3 = decode( 'cp936', 'Incomplete ARP定位表' );
  my $sheet4 = decode( 'cp936', 'IPPHONE_模拟电话 记录表' );
  my $sheet5 = decode( 'cp936', '802.1x 认证PC主机' );
  my $sheet6 = decode( 'cp936', '解析异常 ARP 记录表' );

  my $worksheet1 = $workbook->add_worksheet($sheet1);
  my $worksheet2 = $workbook->add_worksheet($sheet2);
  my $worksheet3 = $workbook->add_worksheet($sheet3);
  my $worksheet4 = $workbook->add_worksheet($sheet4);
  my $worksheet5 = $workbook->add_worksheet($sheet5);
  my $worksheet6 = $workbook->add_worksheet($sheet6);

  #设置表单格式
  my $format = $workbook->add_format();
  $format->set_bold();
  $format->set_color('purple');
  $format->set_bg_color('orange');
  $format->set_size(16);
  $format->set_border(1);
  $format->set_align('center');
  $format->set_valign('vcenter');

  my $format_1 = $workbook->add_format();
  $format_1->set_size(12);
  $format_1->set_border(1);
  $format_1->set_align('center');
  $format_1->set_valign('vcenter');

  my $format_2 = $workbook->add_format();
  $format_2->set_size(12);
  $format_2->set_align('center');
  $format_2->set_valign('vcenter');

  #设置格式
  $worksheet1->set_column( 0, 7, 28.5, $format_2 );
  $worksheet2->set_column( 0, 7, 28.5, $format_2 );
  $worksheet3->set_column( 0, 7, 28.5, $format_2 );
  $worksheet4->set_column( 0, 7, 28.5, $format_2 );
  $worksheet5->set_column( 0, 7, 28.5, $format_2 );
  $worksheet6->set_column( 0, 7, 28.5, $format_2 );

  #创建表单-表头
  my $excel_array = [
    decode( 'cp936', "IP地址" ),
    decode( 'cp936', "关联设备" ),
    decode( 'cp936', "关联接口" ),
    decode( 'cp936', "MAC地址" ),
    decode( 'cp936', "所属VLAN|BD" ),
    decode( 'cp936', "上游网关设备" ),
    decode( 'cp936', "所属VRF" ),
    decode( 'cp936', "所在机房" ),
  ];
  $worksheet1->write_row( 0, 0, $excel_array, $format );
  $worksheet2->write_row( 0, 0, $excel_array, $format );
  $worksheet3->write_row( 0, 0, $excel_array, $format );
  $worksheet4->write_row( 0, 0, $excel_array, $format );
  $worksheet5->write_row( 0, 0, $excel_array, $format );
  $worksheet6->write_row( 0, 0, $excel_array, $format );

  #sheet1写入数据
  my $row1 = 1;
  foreach my $ip ( sort ( keys %{$ret1} ) ) {

    #获取解析结果并分
    my $ip_info   = $ret1->{$ip};
    my $ipaddr    = $ip;
    my $hostname  = $ip_info->{"hostname"};
    my $interface = $ip_info->{"interface"};
    my $mac       = $ip_info->{"mac"};
    my $vlan      = $ip_info->{"vlan"};
    my $dc_code   = $ip_info->{"location"};
    my $vrf       = $ip_info->{"vpn_instance"};
    my $gateway   = $ip_info->{"gate_device"};

    #转换机房数据字典
    my $location;
    if ( exists $location{$dc_code} ) {
      $location = $location{$dc_code};
      $location = decode( 'cp936', $location );
    }
    else {
      $location = decode( 'cp936', "非标命名未解析成功" );
    }

    #临时封装匿名数组
    my $array =
      [ $ipaddr, $hostname, $interface, $mac, $vlan, $gateway, $vrf, $location ];

    #写入计算数据
    $worksheet1->set_row( $row1, 22.5 );
    $worksheet1->write_row( $row1, 0, $array, $format_1 );
    $row1++;
  }

  #sheet2写入数据
  my $row2 = 1;
  foreach my $ip ( sort ( keys %{$ret2} ) ) {

    #获取解析结果并分解
    my $ip_info   = $ret2->{$ip};
    my $ipaddr    = $ip;
    my $hostname  = $ip_info->{"hostname"};
    my $interface = $ip_info->{"interface"};
    my $mac       = $ip_info->{"mac"};
    my $vlan      = $ip_info->{"vlan"};
    my $dc_code   = $ip_info->{"location"};
    my $vrf       = $ip_info->{"vpn_instance"};
    my $gateway   = $ip_info->{"gate_device"};

    #转换机房数据字典
    my $location;
    if ( exists $location{$dc_code} ) {
      $location = $location{$dc_code};
      $location = decode( 'cp936', $location );
    }
    else {
      $location = decode( 'cp936', "非标命名未解析成功" );
    }

    #临时封装匿名数组
    my $array =
      [ $ipaddr, $hostname, $interface, $mac, $vlan, $gateway, $vrf, $location ];

    #写入计算数据
    $worksheet2->set_row( $row2, 22.5 );
    $worksheet2->write_row( $row2, 0, $array, $format_1 );
    $row2++;
  }

  #sheet3写入数据
  my $row3 = 1;
  foreach my $ip ( sort ( keys %{$ret3} ) ) {

    #获取解析结果并分解
    my $ip_info   = $ret3->{$ip};
    my $ipaddr    = $ip;
    my $hostname  = $ip_info->{"hostname"};
    my $interface = $ip_info->{"interface"};
    my $mac       = $ip_info->{"mac"};
    my $vlan      = $ip_info->{"vlan"};
    my $dc_code   = $ip_info->{"location"};
    my $vrf       = $ip_info->{"vpn_instance"};
    my $gateway   = $ip_info->{"gate_device"};

    #转换机房数据字典
    my $location;
    if ( exists $location{$dc_code} ) {
      $location = $location{$dc_code};
      $location = decode( 'cp936', $location );
    }
    else {
      $location = decode( 'cp936', "非标命名未解析成功" );
    }

    #临时封装匿名数组
    my $array =
      [ $ipaddr, $hostname, $interface, $mac, $vlan, $gateway, $vrf, $location ];

    #写入计算数据
    $worksheet3->set_row( $row3, 22.5 );
    $worksheet3->write_row( $row3, 0, $array, $format_1 );
    $row3++;
  }

  #sheet4写入数据
  my $row4 = 1;
  foreach my $ip ( sort ( keys %{$ret4} ) ) {

    #获取解析结果并分解
    my $ip_info   = $ret4->{$ip};
    my $ipaddr    = $ip;
    my $hostname  = $ip_info->{"hostname"};
    my $interface = $ip_info->{"interface"};
    my $mac       = $ip_info->{"mac"};
    my $vlan      = $ip_info->{"vlan"};
    my $dc_code   = $ip_info->{"location"};
    my $vrf       = $ip_info->{"vpn_instance"};
    my $gateway   = $ip_info->{"gate_device"};

    #转换机房数据字典
    my $location;
    if ( exists $location{$dc_code} ) {
      $location = $location{$dc_code};
      $location = decode( 'cp936', $location );
    }
    else {
      $location = decode( 'cp936', "非标命名未解析成功" );
    }

    #临时封装匿名数组
    my $array =
      [ $ipaddr, $hostname, $interface, $mac, $vlan, $gateway, $vrf, $location ];

    #写入计算数据
    $worksheet4->set_row( $row4, 22.5 );
    $worksheet4->write_row( $row4, 0, $array, $format_1 );
    $row4++;
  }

  #sheet+5写入数据
  my $row5 = 1;
  foreach my $ip ( sort ( keys %{$ret5} ) ) {

    #获取解析结果并分解
    my $ip_info   = $ret5->{$ip};
    my $ipaddr    = $ip;
    my $hostname  = $ip_info->{"hostname"};
    my $interface = $ip_info->{"interface"};
    my $mac       = $ip_info->{"mac"};
    my $vlan      = $ip_info->{"vlan"};
    my $dc_code   = $ip_info->{"location"};
    my $vrf       = $ip_info->{"vpn_instance"};
    my $gateway   = $ip_info->{"gate_device"};

    #转换机房数据字典
    my $location;
    if ( exists $location{$dc_code} ) {
      $location = $location{$dc_code};
      $location = decode( 'cp936', $location );
    }
    else {
      $location = decode( 'cp936', "非标命名未解析成功" );
    }

    #临时封装匿名数组
    my $array =
      [ $ipaddr, $hostname, $interface, $mac, $vlan, $gateway, $vrf, $location ];

    #写入计算数据
    $worksheet5->set_row( $row5, 22.5 );
    $worksheet5->write_row( $row5, 0, $array, $format_1 );
    $row5++;
  }

  #sheet+6写入数据
  my $row6 = 1;
  foreach my $ip ( sort ( keys %{$ret6} ) ) {

    #获取解析结果并分解
    my $ip_info   = $ret6->{$ip};
    my $ipaddr    = $ip;
    my $hostname  = $ip_info->{"hostname"};
    my $interface = $ip_info->{"interface"};
    my $mac       = $ip_info->{"mac"};
    my $vlan      = $ip_info->{"vlan"};
    my $dc_code   = $ip_info->{"location"};
    my $vrf       = $ip_info->{"vpn_instance"};
    my $gateway   = $ip_info->{"gate_device"};

    #转换机房数据字典
    my $location;
    if ( exists $location{$dc_code} ) {
      $location = $location{$dc_code};
      $location = decode( 'cp936', $location );
    }
    else {
      $location = decode( 'cp936', "非标命名未解析成功" );
    }

    #临时封装匿名数组
    my $array =
      [ $ipaddr, $hostname, $interface, $mac, $vlan, $gateway, $vrf, $location ];

    #写入计算数据
    $worksheet6->set_row( $row6, 22.5 );
    $worksheet6->write_row( $row6, 0, $array, $format_1 );
    $row6++;
  }

  #持久化数据结构
  my $yaml = "IP归属查询.yaml";
  $yaml = $report_dir . $split . $yaml;
  YAML::DumpFile( $yaml, $ret );
}

#------------------------------------------------------------------------------
#实际处理函数
#------------------------------------------------------------------------------
say "-" x 100 . "\n";
say 'IP地址扫描工具V0.6 BY<careline@126.com>' . "\n";
prepare();
say "-" x 100 . "\n";
write_excel($config_dir);
say "-" x 100 . "\n";