# # Copyright (c) 2001-2011 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary SCP Task Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::MTask::SCP =head1 DESCRIPTION C provide methods to scp(secure copy protocol) functionality on ONTAP and Linux/solaris Host. SCP examples: use NACL::MTask::SCP; # To copy '/mroot/etc/snmppersist.conf' file from filer to linux client. my $scp=NACL::MTask::SCP->new( source_command_interface => $filer_ci, target_command_interface => $unix_ci); my $files=['/mroot/etc/snmppersist.conf']; $scp->copy_from_source_to_target('files_to_copy'=>$files); =cut package NACL::MTask::SCP; use strict; use warnings; use base qw(NACL::MTask::MTask); use Net::SSH2; use Net::IP; use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use Params::Validate qw(validate validate_with SCALAR ARRAYREF UNDEF HASHREF OBJECT BOOLEAN); use NATE::Exceptions::Argument qw(:try); use NACL::APISet::Exceptions::ResponseException qw(:try); use NATE::BaseException qw(:try); use NACL::ComponentUtils qw(Dumper _optional_scalars); use NACL::Tools::httpd::control; use Connectrec qw(); use Hostrec qw(); use constant DEFAULT_TIMEOUT => 120; use constant DEFAULT_PATH => '/x/eng/ctl_logs/pushbutton/general'; use constant DEFAULT_SSH_OPTION => [-o => 'StrictHostKeyChecking=no']; use Class::MethodMaker [ new => [ '-hash', '-init', 'new' ], scalar => [ { -type => 'NACL::C::CommandInterface' }, 'source_command_interface' ], scalar => [ { -type => 'NACL::C::CommandInterface' }, 'target_command_interface' ], scalar => 'target_username', scalar => 'target_password', scalar => 'set_directory_on_target', scalar => 'set_directory_on_source', scalar => 'timeout', scalar => 'files_to_copy', ]; =head1 METHODS =head2 new my $scp = NACL::MTask::SCP->new( source_command_interface => $node, set_directory_on_source => '/mroot/etc/', target_command_interface => $unix_client, set_directory_on_target => '/home/user/', ); Create a SCP object that can be used to transfer the files between two machines. This task uses source_command_interface's machine to run scp command. =over =item Options =over =item C<< source_command_interface => $source_command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of source host. See L =item C<< target_command_interface => $target_command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of target host. See L =item C<< set_directory_on_source => $source_dir >> (Optional) default to '/' Set the source directory on source_command_interface machine to get/put the files. =item C<< set_directory_on_target => $target_dir >> (Optional) default to '/' Set the destinatation directory on target_command_interface machine to get/put the files. =item C<< target_username => $target_machine_username >> (Optional) default to "$apiset->get_connection()->prompt_username()" for filer/client. Sets the username of target machine for the current scp object. This task use this username to get/put the files on source machine. =item C<< target_password => $target_machine_password >> (Optional) default to "$command_interface->hostrec()->default_password()" for client. default to "$systemshell_apiset->get_connection()->prompt_password()" for filer. Sets the password of target machine for the current scp object. This task use this password to get/put the files on source machine. =back =back =cut sub init { $Log->enter() if $may_enter; my $self = shift; my %opts = validate_with( params => \@_, spec => { source_command_interface => { required => 1 }, target_command_interface => { required => 1 } }, allow_extra => 1 ); $Log->debug( sub { " NACL::MTask::SCP Arguments for new" . Dumper( \%opts ) } ); $Log->exit() if $may_exit; } ## end sub init =head2 copy_from_source_to_target (Class method) # 'files_to_copy' copied to 'target host' at $tgt_dir. NACL::MTask::SCP->copy_from_source_to_target( source_command_interface => $src_ci, target_command_interface => $tgt_ci, set_directory_on_source => $src_dir, set_directory_on_target => $tgt_dir, 'files_to_copy' => \@files ); or (Instance method) # 'files_to_copy' copied to 'target host' at $tgt_dir. $scp->copy_from_source_to_target( 'files_to_copy' => \@files, set_directory_on_target => $tgt_dir ); Uses scp to trasnfer files from source host to the target host on set_directory_on_target directory. This method copies the files of source_command_interface's host under set_directory_on_source directory to target_command_interface's host. If it cannot copy all the the files, Exception will be thrown in this case. =over =item Options =over =item C<< files_to_copy => ARRAYREF >> ( Required ) Copies list of files on target host at set_directory_on_target directory. =item C<< source_command_interface => $source_command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of source host. See L =item C<< target_command_interface => $target_command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of target host. See L =item C<< set_directory_on_source => $source_dir >> (Optional) default to '/' Set the source directory of source_command_interface machine to put the files. =item C<< set_directory_on_target => $target_dir >> (Optional) default to '/' Set the destinatation directory of target_command_interface machine to get the files. =item C<< target_username => $target_machine_username >> (Optional) default to "$apiset->get_connection()->prompt_username()" for filer/client. Sets the username of target machine for the current scp object. This task use this username to get/put the files on source machine. =item C<< target_password => $target_machine_password >> (Optional) default to "$command_interface->hostrec()->default_password()" for client. default to "$systemshell_apiset->get_connection()->prompt_password()" for filer. Sets the password of target machine for the current scp object. This task use this password to get/put the files on source machine. =item C<< use_ipv6_address => 0 | 1 >> (Optional, default 0) If set to 1, all copy operations will be performed using IPv6 address. =item C<< timeout => $scalar >> (Optional, Default: 120) Command timeout value =back =back =cut sub copy_from_source_to_target { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { use_ipv6_address => { type => SCALAR, default => 0 } }, no_command_interface => 1, ); # Burt864599, ssh can't establish for the promoted build. # ssh connection can't establish for the promoted build from the # linux interface. In _run_scp_command method scp command is always # executing from source_command_interface, if source_command_interface # is linux machine and target_command_interface is promoted build # interchange the order, and call should be redirect to copy_from_target_to_source # so that connection is always establish from promoted build to linux interface. if ( !$opts{source_command_interface}->isa('NACL::C::CommandInterface::ONTAP') && $opts{target_command_interface}->get_version_manager()->is_promoted() ) { my %other_opts; $other_opts{set_directory_on_source} = delete $opts{set_directory_on_target} if (defined $opts{set_directory_on_target}); $other_opts{set_directory_on_target} = delete $opts{set_directory_on_source} if (defined $opts{set_directory_on_source}); NACL::MTask::SCP->copy_from_target_to_source(%opts, source_command_interface => $opts{target_command_interface}, target_command_interface => $opts{source_command_interface}, %other_opts ); } else { _run_scp_command( 'copy_from_source_to_target', %opts ); } } ## end sub copy_from_source_to_target =head2 copy_from_target_to_source (Class method) # 'files_to_copy' copied to 'source host' at $src_dir from 'target host' NACL::MTask::SCP->copy_from_target_to_source( source_command_interface => $src_ci, target_command_interface => $tgt_ci, set_directory_on_source => $src_dir, set_directory_on_target => $tgt_dir, 'files_to_copy' => \@files ); or (Instance method) # 'files_to_copy' copied to 'target host' at $tgt_dir. $scp->copy_from_target_to_source( 'files_to_copy' => \@files, set_directory_on_source => $tgt_dir ); Uses scp to trasnfer files from target host to the source host on set_directory_on_source directory. This method copies the files of target_command_interface's host under set_directory_on_target directory to source_command_interface's host. =over =item Options =over =item C<< files_to_copy => ARRAYREF >> ( Required ) Copies list of files on source host at set_directory_on_source directory. =item C<< source_command_interface => $source_command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of source host. See L =item C<< target_command_interface => $target_command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of target host. See L =item C<< set_directory_on_source => $source_dir >> (Optional) default to '/' Set the source directory of source_command_interface machine to get the files. =item C<< set_directory_on_target => $target_dir >> (Optional) default to '/' Set the destinatation directory of target_command_interface machine to put the files. =item C<< target_username => $target_machine_username >> (Optional) default to "$apiset->get_connection()->prompt_username()" for filer/client. Sets the username of target machine for the current scp object. This task use this username to get/put the files on source machine. =item C<< target_password => $target_machine_password >> (Optional) default to "$command_interface->hostrec()->default_password()" for client. default to "$systemshell_apiset->get_connection()->prompt_password()" for filer. Sets the password of target machine for the current scp object. This task use this password to get/put the files on source machine. =item C<< use_ipv6_address => 0 | 1 >> (Optional, default 0) If set to 1, all copy operations will be performed using IPv6 address. =item C<< preserve => 0 | 1 >> (Optional, default 1) If set to 1,would preserve modification times, access times, and modes from the original file. =item C<< timeout => $scalar >> (Optional, Default: 120) Command timeout value =back =back =cut sub copy_from_target_to_source { $Log->enter() if $may_enter; my $pkg_or_obj = shift; my %opts = $pkg_or_obj->_common_validate_with( params => \@_, additional_spec => { use_ipv6_address => { type => SCALAR, default => 0 }, preserve => { type => BOOLEAN, default => 1}, }, no_command_interface => 1, ); # Burt864599, ssh can't establish for the promoted build. # ssh connection can't establish for the promoted build from the # linux interface. In _run_scp_command method scp command is always # executing from source_command_interface, if source_command_interface # is linux machine and target_command_interface is promoted build # interchange the order, and call should be redirect to copy_from_source_to_target # so that connection is always establish from promoted build to linux interface. if ( !$opts{source_command_interface}->isa('NACL::C::CommandInterface::ONTAP') && $opts{target_command_interface}->get_version_manager()->is_promoted() ) { # Burt1005446, the preserve argument turned on by default in the additional_spec above # is not a valid parameter for copy_from_source_to_target(), and needs removed before # being applied as part of the overall promoted workaround as part of Burt864599 delete $opts{preserve}; my %other_opts; $other_opts{set_directory_on_source} = delete $opts{set_directory_on_target} if (defined $opts{set_directory_on_target}); $other_opts{set_directory_on_target} = delete $opts{set_directory_on_source} if (defined $opts{set_directory_on_source}); NACL::MTask::SCP->copy_from_source_to_target(%opts, source_command_interface => $opts{target_command_interface}, target_command_interface => $opts{source_command_interface}, %other_opts ); } else { _run_scp_command( 'copy_from_target_to_source', %opts ); } } ## end sub copy_from_target_to_source # Private method that executes scp command sub _run_scp_command { $Log->enter() if $may_enter; my $scp_method = shift; my %opts = @_; $Log->debug( 'Arguments to NACL::MTask::SCP _run_scp_command(via ' . $scp_method . ') method ' . Dumper( \%opts ) ); my $source_ci = delete $opts{source_command_interface}; my $target_ci = delete $opts{target_command_interface}; my $timeout = delete $opts{timeout}|| DEFAULT_TIMEOUT; my $files_to_copy = delete $opts{'files_to_copy'}; my $tgt_username = delete $opts{'target_username'}; my $tgt_password = delete $opts{'target_password'}; my $is_ipv6_addr = delete $opts{use_ipv6_address}; my $preserve = delete $opts{preserve}; my %other_opts; $other_opts{'connectrec-timeout'} = $timeout if ( defined($timeout) ); $other_opts{'preserve'} = $preserve if ( defined($preserve)); my $source_dir; my $target_dir; if ( defined( $opts{set_directory_on_source} ) ) { $source_dir = delete $opts{set_directory_on_source}; $source_dir .= '/' if ( $source_dir !~ /\/$/ ); } if ( defined( $opts{set_directory_on_target} ) ) { $target_dir = delete $opts{set_directory_on_target}; $target_dir .= '/' if ( $target_dir !~ /\/$/ ); } my $files = join ',', @{$files_to_copy}; my $credentials = {}; $credentials->{'username'} = $tgt_username if ( defined($tgt_username) ); $credentials->{'password'} = $tgt_password if ( defined($tgt_password) ); if (!( exists $credentials->{'username'} && exists $credentials->{'password'} ) ) { if ( $target_ci->isa('NACL::C::CommandInterface::ONTAP') ) { my $t_apiset = $target_ci->apiset( category => 'Node', interface => 'CLI', set => "Systemshell" ); my $conn = $t_apiset->get_connection(); $credentials->{'username'} = $conn->prompt_username() if ( !exists $credentials->{'username'} ); $credentials->{'password'} = $conn->prompt_password() if ( !exists $credentials->{'password'} ); } else { $credentials->{'username'} = $target_ci->apiset(interface => 'CLI')->get_connection()->username() if ( !exists $credentials->{'username'} ); $credentials->{'password'} = $target_ci->apiset(interface => 'CLI')->get_connection()->password() if ( !exists $credentials->{'password'} ); #If common username and password does not exist in tharnhost file then #it will try to fetch username and password from ssh connection. my $ssh_conn = $target_ci->hostrec()->conn(); if(defined $ssh_conn) { $credentials->{'username'}=$ssh_conn->{ssh}->{'username'} if ( !$credentials->{'username'} ); $credentials->{'password'}=$ssh_conn->{ssh}->{'password'} if ( !$credentials->{'password'} ); } } ## end else [ if ( $target_ci->isa('NACL::C::CommandInterface::ONTAP'...))] } ## end if ( !( exists $credentials...)) if (!( exists $credentials->{'username'} && exists $credentials->{'password'} ) ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "credentials are missing of target host" . Dumper($credentials) ); } ## end if ( !( exists $credentials...)) my $src_path; my $dst_path; my $target_address = $is_ipv6_addr ? _get_ipv6_net_addr_from_hostrec( command_interface => $target_ci, ) : $target_ci->hostrec()->default_ip(); $target_address = '\[' . $target_address . '\]' if ( Net::IP::ip_is_ipv6($target_address) ); if ( $scp_method eq 'copy_from_target_to_source' ) { $source_dir = '/' if ( !defined($source_dir) ); my $source_files = ($files =~ /,/) ? "\{" .$files . "\}" : $files; $src_path = $credentials->{'username'} . '@' . $target_address . ':' . $target_dir . $source_files; $dst_path = $source_dir; } else { $target_dir = '/' if ( !defined($target_dir) ); $src_path = join ' ', @{$files_to_copy}; $dst_path = $credentials->{'username'} . '@' . $target_address . ':' . $target_dir; } ## end else [ if ( $scp_method eq 'copy_from_target_to_source')] my $apiset; if ( $source_ci->isa('NACL::C::CommandInterface::ONTAP') ) { $apiset = $source_ci->apiset( category => 'Node', interface => 'CLI', set => "Systemshell" ); } else { $apiset = $source_ci->apiset(interface => 'CLI'); } my $scp_output; $apiset->execute_raw_command(command => "cd $source_dir") if (defined($source_dir)); my $single_try_flag = 1; SCP: my $next_cmd; $scp_output = $apiset->scp( 'recursive-copy' => 1, 'source-path' => $src_path, 'dest-path' => $dst_path, 'connectrec-match_table' => [ 'Are you sure you want to continue connecting' => 'yes', 'Please type \'yes\' or \'no\':' => 'yes', '[p|P]assword:' => $credentials->{password}, qr/WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED.*Offending key in (\S+):(\d+)/s => sub { $next_cmd = "sed -i'' '$2 d' $1"; undef } ], %other_opts ); if (defined($next_cmd) && $single_try_flag) { $single_try_flag = 0; $apiset->execute_raw_command(command => $next_cmd); goto SCP; } $scp_output = $scp_output->get_raw_output(); my $file_copied = 0; while ( $scp_output =~ /\s100%\s/gs ) { $file_copied++; } if ( $file_copied < ( $#$files_to_copy + 1 ) ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "All files are not copied... only $file_copied files copied out of " . scalar(@$files_to_copy) ); } ## end if ( $file_copied < ( ...)) $Log->exit() if $may_exit; } ## end sub _run_scp_command sub _object_attributes_validate_spec { return { source_command_interface => { type => OBJECT, isa => 'NACL::C::CommandInterface' }, target_command_interface => { type => OBJECT, isa => 'NACL::C::CommandInterface' }, files_to_copy => { type => ARRAYREF }, _optional_scalars( qw(set_directory_on_target set_directory_on_source timeout target_username target_password) ) }; } ## end sub _object_attributes_validate_spec =head2 copy_to_localhost (Class method) #'files_to_copy' copied to 'localhost' at $src_dir from 'target host' NACL::MTask::SCP->copy_to_localhost( source_command_interface => $src_ci, set_directory_on_source => $src_dir, set_directory_on_target => $tgt_dir, 'files_to_copy' => \@files ); Uses scp to trasnfer files from target host to the localhost and copies the files on set_directory_on_source(default $LOGDIR) directory. If it cannot copy all the the files, Exception will be thrown in this case. on successful completion, It returns the list of copied files with full path. If files_to_copy contains wildcard char(*) or directory, It will return only source_dir with filename example: @copied_files=NACL::MTask::SCP->copy_to_localhost( source_command_interface => $src_ci, set_directory_on_source => '/u/user/', set_directory_on_target => '/tmp/', files_to_copy => ['abc.txt','xyz/', 'testplan*'] ); @copied_files will contain ['/u/user/abc.txt','/u/user/xyz/', '/u/user/testplan*']; IPv6 protocol support is not available for 7mode filer. &mode filers doesn't support scp, so thi stask uses http protocol to copy the files on localhost. =over =item Options =over =item C<< files_to_copy => ARRAYREF >> ( Required ) Copies list of files on source host at set_directory_on_source directory. =item C<< source_command_interface => $source_command_interface >> (Required, isa NACL::C::CommandInterface) The command interface of source. See L =item C<< set_directory_on_source => $source_dir >> (Optional) default to $LOGDIR Set the source directory of source_command_interface machine to get the files. =item C<< set_directory_on_target => $target_dir >> (Optional) default to '/' Set the destinatation directory of localhost machine to put the files. =item C<< source_username => $source_machine_username >> (Optional) default to "$apiset->get_connection()->prompt_username()" for filer/client. Sets the username of source machine for the current scp object. This task use this username to get/put the files on source machine. =item C<< source_password => $source_machine_password >> (Optional) default to "$command_interface->hostrec()->default_password()" for client. default to "$systemshell_apiset->get_connection()->prompt_password()" for filer. Sets the password of source machine for the current scp object. This task use this password to get/put the files on source machine. =item C<< use_ipv6_address => 0 | 1 >> (Optional, default 0) If set to 1, all copy operations will be performed using IPv6 address. =item C<< timeout => $scalar >> (Optional, Default: 120) Command timeout value =item C<< ctl_dir => $dir >> This is the path where the SSH master control socket will be created. For non CTL-PB environments,this defaults to ~/.libnet-openssh-perl. For CTL-PB,tests are run as the user "sti" which has read-only permissions, so the SSH control socket cannot be written to the home directory.For this reason,for CTL-PB runs,path is defaulted to the writable location /x/eng/ctl_logs/pushbutton/general. =item C<< ssh_opts => $ssh_options >> Type: ARRAYREF. (Optional, Default: [-o => 'StrictHostKeyChecking=no']) It is the additional option to pass to the ssh command when establishing the master connection. =back =back =cut sub copy_to_localhost { $Log->enter() if $may_enter; my $pkg = shift; my %opts = validate_with( params => \@_, spec => { source_command_interface => { type => OBJECT, isa => 'NACL::C::CommandInterface', required => 1 }, files_to_copy => { type => ARRAYREF, required => 1 }, set_directory_on_target => { type => SCALAR, optional => 1 }, set_directory_on_source => { type => SCALAR, optional => 1 }, timeout => { type => SCALAR, default => DEFAULT_TIMEOUT }, source_username => { type => SCALAR, optional => 1 }, source_password => { type => SCALAR, optional => 1 }, use_ipv6_address => { type => SCALAR, default => 0 }, ctl_dir => { type => SCALAR, optional => 1 }, ssh_opts => { type => ARRAYREF, default => DEFAULT_SSH_OPTION} }, ); $Log->debug( sub { " NACL::MTask::SCP Arguments for copy_to_localhost" . Dumper( \%opts ); } ); my $source_ci = delete $opts{source_command_interface}; my $timeout = delete $opts{timeout}; my $files = delete $opts{'files_to_copy'}; my @files_to_copy = @{$files}; my $src_username = delete $opts{'source_username'}; my $src_password = delete $opts{'source_password'}; my $is_ipv6_addr = delete $opts{use_ipv6_address}; my $ctl_dir = delete $opts{'ctl_dir'}; my $ssh_opts = delete $opts{'ssh_opts'}; my $source_dir; # For CTL-PB runs the user will be 'sti',So if NATE::OS::login_name() # returns 'sti' then the path defaults to /x/eng/ctl_logs/pushbutton/general. $ctl_dir = DEFAULT_PATH if (NATE::OS::login_name() eq 'sti'); my $target_dir = delete $opts{set_directory_on_target}||Tharn::param('LOGDIR'); $target_dir .= '/' if ( $target_dir !~ m!/$! ); if ( defined( $opts{set_directory_on_source} ) ) { $source_dir = delete $opts{set_directory_on_source}; $source_dir .= '/' if ( $source_dir !~ m!/$! ); @files_to_copy = map { $source_dir . $_ } @files_to_copy; } my @unable_to_copy; my @copied_file; my $ssh_obj; my $host = $is_ipv6_addr ? _get_ipv6_net_addr_from_hostrec( command_interface => $source_ci ) : $source_ci->hostrec()->default_ip(); # Burt864599, ssh can't establish for the promoted build # from the localhost, hence using wget method. if ( ($source_ci->isa('NACL::C::CommandInterface::ONTAP')) && ($source_ci->is_7mode() || $source_ci->get_version_manager()->is_promoted() )) { return _copy_using_wget( ci => $source_ci, host => $host, target_dir => $target_dir, files_to_copy => \@files_to_copy, ); } else { my $credentials = {}; $credentials->{'username'} = $src_username if ( defined($src_username) ); $credentials->{'password'} = $src_password if ( defined($src_password) ); if (!( exists $credentials->{'username'} && exists $credentials->{'password'} ) ) { if ( $source_ci->isa('NACL::C::CommandInterface::ONTAP') ) { my $s_apiset = $source_ci->apiset( category => 'Node', interface => 'CLI', set => "Systemshell" ); my $conn = $s_apiset->get_connection(); $credentials->{'username'} = $conn->prompt_username() if ( !exists $credentials->{'username'} ); $credentials->{'password'} = $conn->prompt_password() if ( !exists $credentials->{'password'} ); } else { $credentials->{'username'} = $source_ci->apiset(interface => 'CLI')->get_connection()->username() if ( !exists $credentials->{'username'} ); $credentials->{'password'} = $source_ci->apiset(interface => 'CLI')->get_connection()->password() if ( !exists $credentials->{'password'} ); } ## end else [ if ( $source_ci->isa('NACL::C::CommandInterface::ONTAP'...))] } ## end if ( !( exists $credentials...)) if (!( exists $credentials->{'username'} && exists $credentials->{'password'} ) ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "credentials are missing of source host" . Dumper($credentials) ); } ## end if ( !( exists $credentials...)) require Net::OpenSSH; $ssh_obj = Net::OpenSSH->new( $host, user => $credentials->{username}, password => $credentials->{password}, master_opts => $ssh_opts, ctl_dir => $ctl_dir, strict_mode => 0 ); # Ran into instances where clients have older version of SSH installed # in /usr/local/bin than in /usr/bin. We really need to be running # Version 4 or above for this code to work see if that is available on # this client if ($ssh_obj->error) { # Check the version of ssh found in path, we need >= 4.0 my $raw_ssh_ver = `bash -c 'ssh -V' 2>&1`; my $ssh_ver; if ($raw_ssh_ver =~ /OpenSSH_(\d)/) { my $ver = $1; if ($ver < 4) { if ($raw_ssh_ver =~ /(OpenSSH_.*?),/) { $ssh_ver = $1; } my $which_ssh = `which ssh`; chomp($which_ssh); $Log->warn( "SSH found in path is $which_ssh, version is $ssh_ver, should be >= OpenSSH_4.0" ); my @possible_ssh = ( qw( /usr/software/bin/ssh /usr/local/bin/ssh /usr/bin/ssh ) ); my $valid_ssh; foreach my $ssh (@possible_ssh) { if (-e $ssh && -x $ssh) { $raw_ssh_ver = `bash -c '$ssh -V' 2>&1`; if ($raw_ssh_ver =~ /OpenSSH_(\d)/) { $ver = $1; if ($ver >= 4) { if ($raw_ssh_ver =~ /(OpenSSH_.*?),/) { $ssh_ver = $1; } $valid_ssh = $ssh; last; } } } } # If a newer version of ssh was found, try it if ($valid_ssh) { $Log->comment( "Try $valid_ssh which is version $ssh_ver"); $ssh_obj = Net::OpenSSH->new( $host, ssh_cmd => $valid_ssh, user => $credentials->{username}, password => $credentials->{password}, master_opts => $ssh_opts, ctl_dir => $ctl_dir, strict_mode => 0 ); } } } } if ( $ssh_obj->error ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "Couldn't establish SSH connection: " . $ssh_obj->error ); } my $scp_opts = {recursive => 1, glob => 1, timeout => $timeout, quiet => 0}; foreach my $_file ( @files_to_copy ) { my $filename = ( $_file =~ m!([^/]+)$! ) ? $1 : ''; if ( !$ssh_obj->scp_get( $scp_opts, $_file, $target_dir ) ) { push @unable_to_copy, $_file; $Log->trace("Unable to copy $_file to $target_dir$filename." . $!); } else { push @copied_file, $target_dir.$filename; } } ## end foreach my $_file ( @files_to_copy...) if ( $#unable_to_copy >= 0 ) { $Log->exit() if $may_exit; NATE::BaseException->throw( "Unable to copy these files" . Dumper( \@unable_to_copy ) ); } } ## end else [ if ( $source_ci->isa('NACL::C::CommandInterface::ONTAP'...))] $Log->exit() if $may_exit; return @copied_file; } ## end sub copy_to_localhost sub _copy_using_wget { $Log->enter() if $may_enter; my %opts = @_; my $httpd = NACL::Tools::httpd::control->new(); my $basename = 'scpdomain'; my $target_dir = $opts{target_dir}; my $testPATH = $target_dir . '/tmp_file_copy_' . rand(); my $apiset = $opts{ci}->apiset( set => "Systemshell" ,interface => 'CLI'); my @copied_files; my @unable_to_copy; my $success; my $host_obj = Hostrec->new( id => 'localhost' ); my $client = Connectrec->new( host => $host_obj, template => "staf" , conntype => "rlogin", conncmd => ["/bin/sh","-i" ]); my $url = $httpd->start( hostrec => $host_obj, peer => $opts{host}, posts => { "/$basename" => $testPATH }, ); foreach my $user_path ( @{ $opts{files_to_copy} } ) { # If the $user_path is /mroot/etc/zoneinfo* # then we need to create the sub directrory structure # zoneinfo/Asia/ # zoneinfo/Africa under $opts{target_dir} # Logic: # If the user path is /mroot/etc/zoneinfo* # Step1 : Remove '/mroot/etc/' as $root_dir from /mroot/etc/zoneinfo* # step2 : find all the files using find command (i.e find /mroot/etc/zoneinfo*) # step3 : Extract sub-directoy and filename fron the find command output: # sub-directory: i.e zoneinfo/Africa, zoneinfo/Asia etc # filename : Accra # Step 1: $user_path =~ /^(.*\/)(.+)$/; my $root_dir = $1; my $user_file_path = $2; # Step 2: find all the file which needs to be downloaded my $out = $apiset->execute_raw_command( command => "find /$user_path -type f" ); my %seen; foreach my $absolute_file_path (split ("\n", $out)) { if ( $absolute_file_path =~ m/No\s*such\s*file\s*or\s*directory/) { push @unable_to_copy, $absolute_file_path; } else { # step 3: $absolute_file_path =~ /$root_dir(.*\/)?(.*)$/; my $sub_dir = ( defined $1 ) ? $target_dir."/".$1 : $target_dir; my $file_name = $2; $Log->trace("Copying $absolute_file_path"); $apiset->execute_raw_command( command => "wget -r $url$basename --post-file $absolute_file_path -O-" ); $Log->trace("create a sub directory to coy the file"); unless ($seen{$sub_dir} ) { #burt1015018 Single quotes are added as directory path may contain special characters my $dir = "'".$sub_dir."'"; $client->execute( command => "mkdir -p $dir", ); $seen{$sub_dir} = 1; } $Log->trace("Move the downloaded file to sub-directory"); $testPATH = "'".$testPATH."'"; my $file = "'".$sub_dir."/".$file_name."'"; $client->execute( command => "mv $testPATH $file", check_status => 1, get_status => \$success ); if ($success) { push @unable_to_copy, $absolute_file_path; } } } push @copied_files, $target_dir.$user_file_path; } $Log->exit() if $may_exit; return @copied_files; } ## end sub _copy_to_localhost_for_7mode sub _get_ipv6_net_addr_from_hostrec { my %opts = @_; my $ci = $opts{command_interface}; my $nettype = $opts{nettype}; my $exclude = [ { family => 'inet4' } ]; my @nets = $ci->hostrec()->net_filter( exclude => $exclude ); return $nets[0]->{address}; } ## end sub _get_ipv6_net_addr_from_hostrec 1;