managementnode/lib/VCL/Module/OS/OSX.pm (990 lines of code) (raw):

#!/usr/bin/perl -w ############################################################################### # $Id$ ############################################################################### # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. ############################################################################### =head1 NAME VCL::Module::OS::OSX.pm - OSX support module =head1 SYNOPSIS Needs to be written =head1 DESCRIPTION This module provides VCL support for OSX operating systems. =cut ############################################################################### package VCL::Module::OS::OSX; # Specify the lib path using FindBin use FindBin; use lib "$FindBin::Bin/../../.."; # Configure inheritance use base qw(VCL::Module::OS); # Specify the version of this module our $VERSION = '2.5.1'; # Specify the version of Perl to use use 5.008000; use strict; use warnings; use diagnostics; use English '-no_match_vars'; use VCL::utils; use File::Basename; #no warnings 'redefine'; ############################################################################### =head1 CLASS VARIABLES =cut =head2 $SOURCE_CONFIGURATION_DIRECTORY Data type : String Description : Location on the management node of the files specific to this OS module which are needed to configure the loaded OS on a computer. This is normally the directory under 'tools' named after this OS module. Example: /opt/vcl/tools/OSX =cut our $SOURCE_CONFIGURATION_DIRECTORY = "$TOOLS/OSX"; =head2 $NODE_CONFIGURATION_DIRECTORY Data type : String Description : Location on computer loaded with a VCL image where configuration files and scripts reside. =cut our $NODE_CONFIGURATION_DIRECTORY = '/var/root/VCL'; ############################################################################### =head1 INTERFACE OBJECT METHODS =cut #////////////////////////////////////////////////////////////////////////////// =head2 pre_capture Parameters : Hash containing 'end_state' key Returns : 1 - success , 0 - failure Description : Performs the steps necessary to prepare a OSX OS before an image is captured. This subroutine is called by a provisioning module's capture() subroutine. The steps performed are: logout and delete users which were created for imaging reservation - done set root password - done set administrator password - done clear tmp files - done disable screen saver if VM - not done disable RDP access ... off by default --- done enable ssh access - done enable ping - not done start firewall -- done shutdown - done =cut sub pre_capture { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return 0; } my $computer_node_name = $self->data->get_computer_node_name(); my $args = shift; # print "*** ".ref($self)."***\n"; # Check if end_state argument was passed if (defined $args->{end_state}) { $self->{end_state} = $args->{end_state}; } else { $self->{end_state} = 'off'; } notify($ERRORS{'OK'}, 0, "beginning OSX image PRE_CAPTURE() preparation tasks on $computer_node_name"); # copy pre_capture configuration files to the computer (scripts, etc) if (!$self->copy_capture_configuration_files()) { notify($ERRORS{'WARNING'}, 0, "unable to copy OSX script files to $computer_node_name"); return 0; } # Log off users which were created for the imaging reservation if (!$self->logoff_users()) { notify($ERRORS{'WARNING'}, 0, "unable to log off all currently logged in users on $computer_node_name"); return 0; } # block rdp via firewall if (!$self->firewall_disable_rdp(1)) { notify($ERRORS{'WARNING'}, 0, "$computer_node_name failed to disable rdp"); return 0; } # Delete the user assigned to this reservation my $deleted_user = $self->delete_user(); if (!$deleted_user) { notify($ERRORS{'WARNING'}, 0, "pre_capture was unable to delete user"); } # set root account password to known value # borrow the WINDOWS_ROOT_PASSW0RD from vcld.conf if (!$self->set_password("root", $WINDOWS_ROOT_PASSWORD)) { notify($ERRORS{'WARNING'}, 0, "unable to set root password"); return 0; } # set administrator account password to known value if (!$self->set_password("administrator", $WINDOWS_ROOT_PASSWORD)) { notify($ERRORS{'WARNING'}, 0, "unable to set root password"); return 0; } # Shutdown node if (!$self->shutdown()) { notify($ERRORS{'WARNING'}, 0, "$computer_node_name failed to shutdown"); return 0; } notify($ERRORS{'OK'}, 0, "pre_capture returning 1"); return 1; } ## end sub pre_capture #////////////////////////////////////////////////////////////////////////////// =head2 post_load Parameters : None Returns : 1 - success , 0 - failure Description : Performs the steps necessary to configure a OSX OS after an image has been loaded. This subroutine is called by a provisioning module's load() subroutine. The steps performed are: wait for ssh to respond -- done wait for root to logout -- not done logout all currently logged on users ... hopefully not needed -- not done # update known_hosts on management node -- not done enable ping on private network -- not done sync time -- not done # remove root password and other private info from vcl config files -- not done randomize root password -- done randomize administrator password -- done imagemeta postoption reboot is set of image ??? -- not done rename computer -- not done computer hostname -- done add line to currentimage.txt indicating post_load has run -- done =cut sub post_load { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return 0; } my $computer_node_name = $self->data->get_computer_node_name(); my $management_node_keys = $self->data->get_management_node_keys(); my $image_name = $self->data->get_image_name(); my $computer_short_name = $self->data->get_computer_short_name(); my $image_os_install_type = $self->data->get_image_os_install_type(); my $imagemeta_postoption = $self->data->get_imagemeta_postoption(); notify($ERRORS{'OK'}, 0, "beginning OSX POST_LOAD() $image_name on $computer_short_name"); # Wait for computer to respond to SSH if (!$self->wait_for_response(15, 900, 8)) { notify($ERRORS{'WARNING'}, 0, "$computer_node_name never responded to SSH"); return 0; } if (!$self->os->update_public_ip_address()) { $self->reservation_failed("failed to update public IP address"); } my $root_random_password = getpw(); if ($self->set_password("root", $root_random_password)) { notify($ERRORS{'OK'}, 0, "successfully changed root password on $computer_node_name"); } else { notify($ERRORS{'WARNING'}, 0, "unable to set root password"); return 0; } my $administrator_random_password = getpw(); if ($self->set_password("administrator", $administrator_random_password)) { notify($ERRORS{'OK'}, 0, "successfully changed administrator password on $computer_node_name"); } else { notify($ERRORS{'WARNING'}, 0, "unable to set administrator password"); return 0; } # Check if the imagemeta postoption is set to reboot, reboot if necessary if ($imagemeta_postoption =~ /reboot/i) { notify($ERRORS{'OK'}, 0, "imagemeta postoption reboot is set for image, rebooting computer"); if (!$self->reboot()) { notify($ERRORS{'WARNING'}, 0, "failed to reboot the computer"); return 0; } } $self->activate_irapp(); # arkurth: added for possible future use, don't have a way to test # Use the following line to enable execution of stage scripts: # return $self->SUPER::post_load(); notify($ERRORS{'OK'}, 0, "returning 1"); return 1; } ## end sub post_load #////////////////////////////////////////////////////////////////////////////// =head2 sanitize Parameters : Returns : 1 - success , 0 - failure Description : revert the changes made when preparing a resource for a particular reservation The steps performed are: if (user logged in) exit Firewall close RDP access delete user =cut sub sanitize { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); notify($ERRORS{'OK'}, 0, "beginning OSX SANITIZE() on $computer_node_name"); # block rdp via firewall if (!$self->firewall_disable_rdp()) { notify($ERRORS{'WARNING'}, 0, "$computer_node_name failed to disable rdp"); return 0; } # Delete user associated with the reservation if ($self->delete_user()) { notify($ERRORS{'OK'}, 0, "users have been deleted from $computer_node_name"); return 1; } else { notify($ERRORS{'WARNING'}, 0, "failed to delete users from $computer_node_name"); return 0; } notify($ERRORS{'OK'}, 0, "$computer_node_name has been sanitized"); return 1; } ## end sub sanitize #////////////////////////////////////////////////////////////////////////////// =head2 reboot Parameters : $wait_for_reboot Returns : 1 - success , 0 - failure Description : The steps performed are: graceful reboot of OS force logout of users wait for reboot to complete returns after reboot is complete make sure ssh is enabled make sure ping is enabled reboot wait for ssh to be up =cut sub reboot { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); notify($ERRORS{'OK'}, 0, "beginning OSX REBOOT() on $computer_node_name"); # Check if an argument was supplied my $wait_for_reboot = shift; if (!defined($wait_for_reboot) || $wait_for_reboot !~ /0/) { notify($ERRORS{'DEBUG'}, 0, "rebooting $computer_node_name and waiting for ssh to become active"); $wait_for_reboot = 1; } else { notify($ERRORS{'DEBUG'}, 0, "rebooting $computer_node_name and NOT waiting for ssh to become active"); $wait_for_reboot = 0; } my $reboot_start_time = time(); notify($ERRORS{'DEBUG'}, 0, "reboot will be attempted on $computer_node_name"); # Check if computer responds to ssh before preparing for reboot if ($self->wait_for_ssh(0)) { # Make sure SSH access is enabled from private IP addresses my $reboot_command = "/sbin/shutdown -r now"; my ($reboot_exit_status, $reboot_output) = $self->execute($reboot_command,1); if (!defined($reboot_output)) { notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command to reboot $computer_node_name"); return 0; } if ($reboot_exit_status == 0) { notify($ERRORS{'OK'}, 0, "executed reboot command on $computer_node_name"); } else { notify($ERRORS{'WARNING'}, 0, "failed to reboot $computer_node_name, attempting power reset, output:\n" . join("\n", @$reboot_output)); # Call provisioning module's power_reset() subroutine if ($self->provisioner->power_reset()) { notify($ERRORS{'OK'}, 0, "initiated power reset on $computer_node_name"); } else { notify($ERRORS{'WARNING'}, 0, "reboot failed, failed to initiate power reset on $computer_node_name"); return 0; } } } else { # Computer did not respond to ssh notify($ERRORS{'WARNING'}, 0, "$computer_node_name did not respond to ssh, graceful reboot cannot be performed, attempting hard reset"); # Call provisioning module's power_reset() subroutine if ($self->provisioner->power_reset()) { notify($ERRORS{'OK'}, 0, "initiated power reset on $computer_node_name"); } else { notify($ERRORS{'WARNING'}, 0, "reboot failed, failed to initiate power reset on $computer_node_name"); return 0; } } ## end else [ if ($self->wait_for_ssh(0)) # Check if wait for reboot is set if (!$wait_for_reboot) { return 1; } my $wait_attempt_limit = 2; if ($self->wait_for_reboot($wait_attempt_limit)) { # Reboot was successful, calculate how long reboot took my $reboot_end_time = time(); my $reboot_duration = ($reboot_end_time - $reboot_start_time); notify($ERRORS{'OK'}, 0, "reboot complete on $computer_node_name, took $reboot_duration seconds"); return 1; } else { notify($ERRORS{'WARNING'}, 0, "reboot failed on $computer_node_name, made $wait_attempt_limit attempts"); return 0; } } ## end sub reboot #////////////////////////////////////////////////////////////////////////////// =head2 shutdown Parameters : Returns : 1 - success , 0 - failure Description : The steps performed are: graceful shutdown of OS -- done force users to logout -- not done waits for shutdown to complete -- done returns after complete -- done # pre_capture =cut sub shutdown { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); notify($ERRORS{'OK'}, 0, "beginning OSX SHUTDOWN() on $computer_node_name"); my $command = '/sbin/shutdown -h now'; my ($exit_status, $output) = $self->execute($command,1); if (defined $exit_status && $exit_status == 0) { notify($ERRORS{'DEBUG'}, 0, "executed command to shut down $computer_node_name"); } else { if (!defined($output)) { notify($ERRORS{'WARNING'}, 0, "failed to execute command to shut down $computer_node_name, attempting power off"); } else { notify($ERRORS{'WARNING'}, 0, "failed to shut down $computer_node_name, attempting power off, output:\n" . join("\n", @$output)); } # Call provisioning module's power_off() subroutine if (!$self->provisioner->power_off()) { notify($ERRORS{'WARNING'}, 0, "failed to shut down $computer_node_name, failed to initiate power off"); return; } } # Wait maximum of 3 minutes for the computer to become unresponsive if (!$self->wait_for_no_ping(180)) { # Computer never stopped responding to ping notify($ERRORS{'WARNING'}, 0, "$computer_node_name never became unresponsive to ping after shutdown command was issued"); return; } # Wait maximum of 5 minutes for computer to power off my $power_off = $self->provisioner->wait_for_power_off(300); if (!defined($power_off)) { # wait_for_power_off result will be undefined if the provisioning module doesn't implement a power_status subroutine notify($ERRORS{'OK'}, 0, "unable to determine power status of $computer_node_name from provisioning module, sleeping 1 minute to allow computer time to shutdown"); sleep 60; } elsif (!$power_off) { notify($ERRORS{'WARNING'}, 0, "$computer_node_name never powered off"); return; } return 1; } ## end sub shutdown #////////////////////////////////////////////////////////////////////////////// =head2 reserve Parameters : Returns : 1 - success , 0 - failure Description : adds user to image The steps performed are: if (!administrator !root) useradd set password =cut sub reserve { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return 0; } my $reservation_password = $self->data->get_reservation_password(); my $username = $self->data->get_user_login_id(); my $computer_node_name = $self->data->get_computer_node_name(); notify($ERRORS{'OK'}, 0, "beginning OSX RESERVE() on $computer_node_name"); # Add the users to the computer # The add_users() subroutine will add the reservation user if ($self->add_user()) { notify($ERRORS{'OK'}, 0, "Successfully added useracct: $username on $computer_node_name"); } else { notify($ERRORS{'CRITICAL'}, 0, "Failed to add useracct: $username on $computer_node_name"); return 0; } notify($ERRORS{'OK'}, 0, "returning 1"); return 1; } ## end sub reserve #////////////////////////////////////////////////////////////////////////////// =head2 grant_access Parameters : called as an object Returns : 1 - success , 0 - failure Description : opens port in firewall for external access # # gets called by reserved.pm after the user has clicked "Connect" # the user's IP address is known when called # opens firewall for RDP # =cut sub grant_access { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return 0; } my $user = $self->data->get_user_login_id(); my $computer_node_name = $self->data->get_computer_node_name(); my $remote_ip = $self->data->get_reservation_remote_ip(); my $request_forimaging = $self->data->get_request_forimaging(); notify($ERRORS{'OK'}, 0, "GRANT_ACCESS() routine $user,$computer_node_name"); # Check to make sure remote IP is defined my $remote_ip_range; if (!$remote_ip) { notify($ERRORS{'WARNING'}, 0, "reservation remote IP address is not set in the data structure, opening RDP to any address"); } elsif ($remote_ip !~ /^(\d{1,3}\.?){4}$/) { notify($ERRORS{'WARNING'}, 0, "reservation remote IP address format is invalid: $remote_ip, opening RDP to any address"); } else { # Assemble the IP range string in CIDR notation $remote_ip_range = "$remote_ip/24"; notify($ERRORS{'OK'}, 0, "RDP will be allowed from $remote_ip_range on $computer_node_name"); } # Set the $remote_ip_range variable to the string 'all' if it isn't already set (for display purposes) $remote_ip_range = 'any' if !$remote_ip_range; # Allow RDP connections if ($request_forimaging) { if ($self->firewall_enable_rdp($remote_ip_range,1)) { notify($ERRORS{'OK'}, 0, "firewall was configured to allow RDP access from $remote_ip_range on $computer_node_name"); } else { notify($ERRORS{'WARNING'}, 0, "firewall could not be configured to grant RDP access from $remote_ip_range on $computer_node_name"); return 0; } } else { if ($self->firewall_enable_rdp($remote_ip_range)) { notify($ERRORS{'OK'}, 0, "firewall was configured to allow RDP access from $remote_ip_range on $computer_node_name"); } else { notify($ERRORS{'WARNING'}, 0, "firewall could not be configured to grant RDP access from $remote_ip_range on $computer_node_name"); return 0; } } notify($ERRORS{'OK'}, 0, "access has been granted for reservation on $computer_node_name"); return 1; } ## end sub grant_access #////////////////////////////////////////////////////////////////////////////// =head2 enable_firewall_port Parameters : $protocol, $port, $scope (optional) Returns : 1 if succeeded, 0 otherwise Description : Enables a firewall port on the computer. The protocol and port arguments are required. An optional scope argument may supplied. # called by OS::process_connect_methods() =cut sub enable_firewall_port { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); notify($ERRORS{'OK'}, 0, " beginning OSX ENABLE_FIREWALL_PORT()"); my $protocol = shift; if (!$protocol) { notify($ERRORS{'WARNING'}, 0, " protocol variable was not passed as an argument"); return 0; } my $port = shift; if (!$port) { notify($ERRORS{'WARNING'}, 0, " port variable was not passed as an argument"); return 0; } my $scope = shift; if (!$scope) { $scope = 'all'; } my $command = "ipfw list"; my ($status, $output) = $self->execute($command, 1); notify($ERRORS{'DEBUG'}, 0, " checking firewall rules on node $computer_node_name"); my $rule=0; my $upper_limit=12300; my $found=0; while ($rule == 0 && $upper_limit > 0) { foreach my $line (@{$output}) { if ($line =~ /^$upper_limit\s+/) { $found=1; } } if ($found) { $upper_limit--; $found=0; } else { $rule = $upper_limit; } } $command = "ipfw add $rule allow $protocol from $scope to any dst-port $port"; ($status, $output) = $self->execute($command, 1); notify($ERRORS{'DEBUG'}, 0, "checking connections on node $computer_node_name on port $port"); return 1; } ## end sub enable_firewall_port #////////////////////////////////////////////////////////////////////////////// =head2 get_cpu_core_count Parameters : none Returns : integer Description : Retrieves the number of CPU cores the computer has by querying the NUMBER_OF_PROCESSORS environment variable. # called by Provisioning::VMware:VMware.pm # Windows.pm only returns value from database # return $self->get_environment_variable_value('NUMBER_OF_PROCESSORS'); =cut sub get_cpu_core_count { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); my $num_cpus = 0; my $command = "/usr/sbin/system_profiler SPHardwareDataType"; # Hardware: # # Hardware Overview: # # Model Name: Mac mini # Model Identifier: Macmini2,1 # Processor Speed: 2.66 GHz # Number Of Processors: 2 # Total Number Of Cores: 2 # L2 Cache (per processor): 4 MB # Memory: 7.88 GB # Bus Speed: 367 MHz # Boot ROM Version: MM21.009A.B00 # SMC Version (system): 1.30f3 # Serial Number (system): SOMESRLNMBR # Hardware UUID: 9D002E7C-B39B-590F-B9E7-A7AE1554F9E2 my ($status, $output) = $self->execute($command, 1); notify($ERRORS{'DEBUG'}, 0, " getting cpu count on node $computer_node_name "); foreach my $line (@{$output}) { if ($line =~ /\s+(Total)\s+(Number)\s+(Of)\s+(Cores:)\s+([0-9]*)/) { $num_cpus = $line; $num_cpus =~ s/ Total Number Of Cores: //; } } notify($ERRORS{'DEBUG'}, 0, " get_cpu_core_count() is $num_cpus"); return $num_cpus; } #////////////////////////////////////////////////////////////////////////////// =head2 check_connection_on_port Parameters : $port Returns : (connected|conn_wrong_ip|timeout|failed) Description : uses netstat to see if any thing is connected to the provided port # called by OS.pm:is_user_connected() =cut sub check_connection_on_port { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); my $remote_ip = $self->data->get_reservation_remote_ip(); my $computer_public_ip_address = $self->data->get_computer_public_ip_address(); my $port = shift; if (!$port) { notify($ERRORS{'WARNING'}, 0, "port variable was not passed as an argument"); return "failed"; } my $ret_val = "no"; my $command = "netstat -an"; my ($status, $output) = $self->execute($command, 1); notify($ERRORS{'DEBUG'}, 0, "checking connections on node $computer_node_name on port $port"); foreach my $line (@{$output}) { if ($line =~ /tcp4\s+([0-9]*)\s+([0-9]*)\s+($computer_public_ip_address.$port)\s+($remote_ip).([0-9]*)(.*)(ESTABLISHED)/) { $ret_val = "connected"; } } return $ret_val; } #////////////////////////////////////////////////////////////////////////////// =head2 user_exists Parameters : Returns : Description : =cut sub user_exists { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); # Attempt to get the username from the arguments # If no argument was supplied, use the user specified in the DataStructure my $username = shift; if (!$username) { $username = $self->data->get_user_login_id(); } notify($ERRORS{'DEBUG'}, 0, "checking if user $username exists on $computer_node_name"); # Attempt to query the user account my $query_user_command = "id $username"; my ($query_user_exit_status, $query_user_output) = $self->execute($query_user_command,1); if (grep(/uid/, @$query_user_output)) { notify($ERRORS{'DEBUG'}, 0, "user $username exists on $computer_node_name"); return 1; } elsif (grep(/No such user/i, @$query_user_output)) { notify($ERRORS{'DEBUG'}, 0, "user $username does not exist on $computer_node_name"); return 0; } elsif (defined($query_user_exit_status)) { notify($ERRORS{'WARNING'}, 0, "failed to determine if user $username exists on $computer_node_name, exit status: $query_user_exit_status, output:\n@{$query_user_output}"); return; } else { notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to determine if user $username exists on $computer_node_name"); return; } } ############################################################################### # # # END OF GLOBALLY REQUIRED OS MODULE SUBROUTINES # # # ############################################################################### =head1 AUXILIARY OBJECT METHODS =cut #////////////////////////////////////////////////////////////////////////////// =head2 get_node_configuration_directory Parameters : none Returns : string Description : Retrieves the $NODE_CONFIGURATION_DIRECTORY variable value for the OS. This is the path on the computer's hard drive where image configuration files and scripts are copied. =cut sub get_node_configuration_directory { return $NODE_CONFIGURATION_DIRECTORY; } #////////////////////////////////////////////////////////////////////////////// =head2 copy_capture_configuration_files Parameters : $source_configuration_directory Returns : Description : Copies all required configuration files to the computer, including scripts, needed to capture an image. # from pre_capture =cut sub copy_capture_configuration_files { my $self = shift; unless (ref($self) && $self->isa('VCL::Module')) { notify($ERRORS{'CRITICAL'}, 0, "subroutine can only be called as a VCL module object method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); my $management_node_keys = $self->data->get_management_node_keys(); my $command = "/bin/chmod -R 755 $NODE_CONFIGURATION_DIRECTORY"; # Get an array containing the configuration directory paths on the management node # This is made up of all the the $SOURCE_CONFIGURATION_DIRECTORY values for the OS class and it's parent classes # The first array element is the value from the top-most class the OS object inherits from my @source_configuration_directories = $self->get_source_configuration_directories(); if (!@source_configuration_directories) { notify($ERRORS{'WARNING'}, 0, "unable to retrieve source configuration directories"); return; } # Delete existing configuration directory if it exists if (!$self->delete_capture_configuration_files()) { notify($ERRORS{'WARNING'}, 0, "unable to delete existing capture configuration files"); return; } # Attempt to create the configuration directory if it doesn't already exist if (!$self->create_directory($NODE_CONFIGURATION_DIRECTORY)) { notify($ERRORS{'WARNING'}, 0, "unable to create directory on $computer_node_name: $NODE_CONFIGURATION_DIRECTORY"); return; } # Copy configuration files for my $source_configuration_directory (@source_configuration_directories) { # Check if source configuration directory exists on this management node unless (-d "$source_configuration_directory") { notify($ERRORS{'OK'}, 0, "source directory does not exist on this management node: $source_configuration_directory"); next; } notify($ERRORS{'OK'}, 0, "copying image capture configuration files from $source_configuration_directory to $computer_node_name"); if (run_scp_command("$source_configuration_directory/*", "$computer_node_name:$NODE_CONFIGURATION_DIRECTORY", $management_node_keys)) { notify($ERRORS{'OK'}, 0, "copied $source_configuration_directory directory to $computer_node_name:$NODE_CONFIGURATION_DIRECTORY"); notify($ERRORS{'DEBUG'}, 0, "attempting to set permissions on $computer_node_name:$NODE_CONFIGURATION_DIRECTORY"); if ($self->execute($command,1)) { notify($ERRORS{'OK'}, 0, "chmoded -R 755 $computer_node_name:$NODE_CONFIGURATION_DIRECTORY"); } else { notify($ERRORS{'WARNING'}, 0, "could not chmod -R 777 $computer_node_name:$NODE_CONFIGURATION_DIRECTORY"); return; } } ## end if (run_scp_command("$source_configuration_directory/*"... else { notify($ERRORS{'WARNING'}, 0, "failed to copy $source_configuration_directory to $computer_node_name"); return; } } return 1; } ## end sub copy_capture_configuration_files #////////////////////////////////////////////////////////////////////////////// =head2 delete_capture_configuration_files Parameters : Returns : Description : Deletes the capture configuration directory. # copy_capture_configuration_files =cut sub delete_capture_configuration_files { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Remove existing configuration files if they exist notify($ERRORS{'OK'}, 0, "attempting to remove old configuration directory if it exists: $NODE_CONFIGURATION_DIRECTORY"); if (!$self->delete_file($NODE_CONFIGURATION_DIRECTORY)) { notify($ERRORS{'WARNING'}, 0, "unable to remove existing configuration directory: $NODE_CONFIGURATION_DIRECTORY"); return 0; } return 1; } #////////////////////////////////////////////////////////////////////////////// =head2 delete_user Parameters : Returns : Description : =cut sub delete_user { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return 0; } my $computer_node_name = $self->data->get_computer_node_name(); # Make sure the user login ID was passed my $user_login_id = shift; $user_login_id = $self->data->get_user_login_id() if (!$user_login_id); if (!$user_login_id) { notify($ERRORS{'WARNING'}, 0, "user could not be determined"); return 0; } if ($user_login_id eq "root" || $user_login_id eq "administrator" ) { notify($ERRORS{'WARNING'}, 0, "$user_login_id MUST not be deleted"); return 0; } my $userdel_cmd = $self->get_node_configuration_directory() . "/userdel $user_login_id"; if ($self->execute($userdel_cmd,1)) { notify($ERRORS{'DEBUG'}, 0, "deleted user: $user_login_id from $computer_node_name"); } else { notify($ERRORS{'DEBUG'}, 0, "failed to delete user: $user_login_id from $computer_node_name"); } return 1; } ## end sub delete_user #////////////////////////////////////////////////////////////////////////////// =head2 set_password Parameters : $username, $password Returns : 1 - success , 0 - failure Description : sets password for given username # pre_capture =cut sub set_password { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return 0; } my $computer_node_name = $self->data->get_computer_node_name(); my $username = shift; my $password = shift; # If no argument was supplied, use the user specified in the DataStructure if (!defined($username)) { $username = $self->data->get_user_logon_id(); } if (!defined($password)) { $password = $self->data->get_reservation_password(); } # Make sure both the username and password were determined if (!defined($username) || !defined($password)) { notify($ERRORS{'WARNING'}, 0, "username and password could not be determined"); return 0; } # Attempt to set the password notify($ERRORS{'DEBUG'}, 0, "setting password of $username to $password on $computer_node_name"); my $passwd_cmd = "/usr/bin/dscl . -passwd /Users/$username '$password'"; my ($exit_status1, $output1) = $self->execute($passwd_cmd,1); if ($exit_status1 == 0) { notify($ERRORS{'OK'}, 0, "password changed to '$password' for user '$username' on $computer_node_name"); } elsif (defined $exit_status1) { notify($ERRORS{'WARNING'}, 0, "failed to change password to '$password' for user '$username' on $computer_node_name, exit status: $exit_status1, output:\n@{$output1}"); return 0; } else { notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to change password to '$password' for user '$username' on $computer_node_name"); return 0; } # Attempt to remove the login.keychain if ("$username" eq "administrator" || "$username" eq "root") { notify($ERRORS{'DEBUG'}, 0, "removing login.keychain of $username on $computer_node_name"); my $command2 = "find ~$username/Library/Keychains -type f -name login.keychain -exec rm {} \\;"; # my $command2 = "/bin/rm /Users/$username/Library/Keychains/login.keychain"; my ($exit_status2, $output2) = $self->execute($command2,1); if ($exit_status2 == 0) { notify($ERRORS{'OK'}, 0, "removed login.keychain for user '$username' on $computer_node_name"); } elsif (defined $exit_status2) { notify($ERRORS{'WARNING'}, 0, "failed to remove login.keychain for user '$username' on $computer_node_name, exit status: $exit_status2, output:\n@{$output2}"); return 0; } else { notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to remove login.keychain for user '$username' on $computer_node_name"); return 0; } } notify($ERRORS{'OK'}, 0, "changed password for user: $username"); return 1; } ## end sub set_password #////////////////////////////////////////////////////////////////////////////// =head2 file_exists Parameters : $path Returns : boolean Description : Checks if a file or directory exists on the OSX computer. # delete_file =cut sub file_exists { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the path from the subroutine arguments and make sure it was passed my $path = shift; if (!$path) { notify($ERRORS{'WARNING'}, 0, "path argument was not specified"); return; } # Remove any quotes from the beginning and end of the path $path = normalize_file_path($path); # Escape all spaces in the path my $escaped_path = escape_file_path($path); my $computer_short_name = $self->data->get_computer_short_name(); # Check if the file or directory exists # Do not enclose the path in quotes or else wildcards won't work my $command = "stat $escaped_path"; my ($exit_status, $output) = $self->execute($command,1); if (!defined($output)) { notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to determine if file or directory exists on $computer_short_name:\npath: '$path'\ncommand: '$command'"); return; } elsif (grep(/no such file/i, @$output)) { notify($ERRORS{'DEBUG'}, 0, "file or directory does not exist on $computer_short_name: '$path'"); return 0; } elsif (grep(/stat: /i, @$output)) { notify($ERRORS{'WARNING'}, 0, "failed to determine if file or directory exists on $computer_short_name:\npath: '$path'\ncommand: '$command'\nexit status: $exit_status, output:\n" . join("\n", @$output)); return; } # Count the lines beginning with "Size:" and ending with "file", "directory", or "link" to determine how many files and/or directories were found my $files_found = grep(/^\s*Size:.*file$/i, @$output); my $directories_found = grep(/^\s*Size:.*directory$/i, @$output); my $links_found = grep(/^\s*Size:.*link$/i, @$output); if ($files_found || $directories_found || $links_found) { notify($ERRORS{'DEBUG'}, 0, "'$path' exists on $computer_short_name, files: $files_found, directories: $directories_found, links: $links_found"); return 1; } else { notify($ERRORS{'WARNING'}, 0, "unexpected output returned while attempting to determine if file or directory exists on $computer_short_name: '$path'\ncommand: '$command'\nexit status: $exit_status, output:\n" . join("\n", @$output)); return; } } #////////////////////////////////////////////////////////////////////////////// =head2 delete_file Parameters : $path Returns : boolean Description : Deletes files or directories on the OSX computer. =cut sub delete_file { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the path argument my $path = shift; if (!$path) { notify($ERRORS{'WARNING'}, 0, "path argument were not specified"); return; } # Remove any quotes from the beginning and end of the path $path = normalize_file_path($path); # Escape all spaces in the path my $escaped_path = escape_file_path($path); my $computer_short_name = $self->data->get_computer_short_name(); # Delete the file my $command = "rm -rfv $escaped_path"; my ($exit_status, $output) = $self->execute($command,1); if (!defined($output)) { notify($ERRORS{'WARNING'}, 0, "failed to run command to delete file or directory on $computer_short_name:\npath: '$path'\ncommand: '$command'"); return; } elsif (grep(/(cannot access|no such file)/i, @$output)) { notify($ERRORS{'OK'}, 0, "file or directory not deleted because it does not exist on $computer_short_name: $path"); } elsif (grep(/rm: /i, @$output)) { notify($ERRORS{'WARNING'}, 0, "error occurred attempting to delete file or directory on $computer_short_name: '$path':\ncommand: '$command'\nexit status: $exit_status\noutput:\n" . join("\n", @$output)); } else { notify($ERRORS{'OK'}, 0, "deleted '$path' on $computer_short_name"); } # Make sure the path does not exist my $file_exists = $self->file_exists($path); if (!defined($file_exists)) { notify($ERRORS{'WARNING'}, 0, "failed to confirm file doesn't exist on $computer_short_name: '$path'"); return; } elsif ($file_exists) { notify($ERRORS{'WARNING'}, 0, "file was not deleted, it still exists on $computer_short_name: '$path'"); return; } else { notify($ERRORS{'DEBUG'}, 0, "confirmed file does not exist on $computer_short_name: '$path'"); return 1; } } #////////////////////////////////////////////////////////////////////////////// =head2 create_directory Parameters : $directory_path, $mode (optional) Returns : boolean Description : Creates a directory on the OSX computer as indicated by the $directory_path argument. # copy_capture_configuration_files =cut sub create_directory { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Get the directory path argument my $directory_path = shift; if (!$directory_path) { notify($ERRORS{'WARNING'}, 0, "directory path argument was not supplied"); return; } # Remove any quotes from the beginning and end of the path $directory_path = normalize_file_path($directory_path); my $computer_short_name = $self->data->get_computer_short_name(); # Attempt to create the directory # my $command = "ls -d --color=never \"$directory_path\" 2>&1 || mkdir -p \"$directory_path\" 2>&1 && ls -d --color=never \"$directory_path\""; my $command = "ls -d \"$directory_path\" 2>&1 || mkdir -p \"$directory_path\" 2>&1 && ls -d \"$directory_path\""; my ($exit_status, $output) = $self->execute($command,1); if (!defined($output)) { notify($ERRORS{'WARNING'}, 0, "failed to run command to create directory on $computer_short_name:\npath: '$directory_path'\ncommand: '$command'"); return; } elsif (grep(/mkdir:/i, @$output)) { notify($ERRORS{'WARNING'}, 0, "error occurred attempting to create directory on $computer_short_name: '$directory_path':\ncommand: '$command'\nexit status: $exit_status\noutput:\n" . join("\n", @$output)); return; } elsif (grep(/^\s*$directory_path\s*$/, @$output)) { if (grep(/ls:/, @$output)) { notify($ERRORS{'OK'}, 0, "directory created on $computer_short_name: '$directory_path'"); } else { notify($ERRORS{'OK'}, 0, "directory already exists on $computer_short_name: '$directory_path'"); } return 1; } else { notify($ERRORS{'WARNING'}, 0, "unexpected output returned from command to create directory on $computer_short_name: '$directory_path':\ncommand: '$command'\nexit status: $exit_status\noutput:\n" . join("\n", @$output) . "\nlast line:\n" . string_to_ascii(@$output[-1])); return; } } #////////////////////////////////////////////////////////////////////////////// =head2 firewall_enable_rdp Parameters : Returns : 1 if succeeded, 0 otherwise Description : # grant_access =cut sub firewall_enable_rdp { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); my $remote_ip_range = shift; my $persist = shift; my $fw_enable_rdp_cmd = ""; # Make sure the remote ip range was passed if (!$remote_ip_range) { notify($ERRORS{'CRITICAL'}, 0, "remote IP range could not be determined, failed to open RDP on $computer_node_name"); return 0; } if ($persist) { $fw_enable_rdp_cmd = $self->get_node_configuration_directory() . "/fw_enable_rdp $remote_ip_range $persist"; } else { $fw_enable_rdp_cmd = $self->get_node_configuration_directory() . "/fw_enable_rdp $remote_ip_range"; } if ($self->execute($fw_enable_rdp_cmd,1)) { notify($ERRORS{'DEBUG'}, 0, "enabled rdp through firewall on $computer_node_name"); } else { notify($ERRORS{'DEBUG'}, 0, "failed to enable rdp through firewall on $computer_node_name"); } return 1; } ## end sub firewall_enable_rdp #////////////////////////////////////////////////////////////////////////////// =head2 firewall_disable_rdp Parameters : optional persistence flag Returns : 1 if succeeded, 0 otherwise Description : # pre_capture # sanitize =cut sub firewall_disable_rdp { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); my $persist = shift; my $fw_disable_rdp_cmd; if ($persist) { $fw_disable_rdp_cmd = $self->get_node_configuration_directory() . "/fw_disable_rdp $persist"; } else { $fw_disable_rdp_cmd = $self->get_node_configuration_directory() . "/fw_disable_rdp"; } if ($self->execute($fw_disable_rdp_cmd,1)) { notify($ERRORS{'DEBUG'}, 0, "disabled rdp through firewall on $computer_node_name"); } else { notify($ERRORS{'DEBUG'}, 0, "failed to disable rdp through firewall on $computer_node_name"); } return 1; } ## end sub firewall_disable_rdp #////////////////////////////////////////////////////////////////////////////// =head2 logoff_users Parameters : Returns : 1 if succeeded, 0 otherwise Description : # pre_capture =cut sub logoff_users { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return 0; } my $computer_node_name = $self->data->get_computer_node_name(); my $logout_users_cmd = "/usr/bin/killall loginwindow"; if ($self->execute($logout_users_cmd,1)) { notify($ERRORS{'DEBUG'}, 0, "logged off all users on $computer_node_name"); } else { notify($ERRORS{'WARNING'}, 0, "failed to log off all users on $computer_node_name"); } return 1; } ## end sub logoff_users #////////////////////////////////////////////////////////////////////////////// =head2 get_private_mac_address Parameters : none Returns : string Description : Returns the MAC address of the interface assigned the private IP address. =cut sub get_private_mac_address { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $private_network_configuration = $self->get_network_configuration('private'); if (!$private_network_configuration) { notify($ERRORS{'WARNING'}, 0, "failed to retrieve private network configuration"); return; } my $private_mac_address = $private_network_configuration->{physical_address}; if (!$private_mac_address) { notify($ERRORS{'WARNING'}, 0, "'physical_address' key is not set in the private network configuration hash:\n" . format_data($private_network_configuration)); return; } notify($ERRORS{'DEBUG'}, 0, "retrieved private MAC address: $private_mac_address"); return $private_mac_address; } #////////////////////////////////////////////////////////////////////////////// =head2 get_public_mac_address Parameters : none Returns : string Description : Returns the MAC address of the interface assigned the public IP address. =cut sub get_public_mac_address { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $public_network_configuration = $self->get_network_configuration('public'); if (!$public_network_configuration) { notify($ERRORS{'WARNING'}, 0, "failed to retrieve public network configuration"); return; } my $public_mac_address = $public_network_configuration->{physical_address}; if (!$public_mac_address) { notify($ERRORS{'WARNING'}, 0, "'physical_address' key is not set in the public network configuration hash:\n" . format_data($public_network_configuration)); return; } notify($ERRORS{'DEBUG'}, 0, "retrieved public MAC address: $public_mac_address"); return $public_mac_address; } #////////////////////////////////////////////////////////////////////////////// =head2 get_network_configuration Parameters : $network_type (optional) Returns : hash reference Description : Retrieves the network configuration on the OSX computer and constructs a hash. A $network_type argument can be supplied containing either 'private' or 'public'. If the $network_type argument is not supplied, the hash keys are the network interface names and the hash reference returned is formatted as follows: |--%{eth0} |--%{eth0}{ip_address} |--{eth0}{ip_address}{10.10.4.35} = '255.255.240.0' |--{eth0}{name} = 'eth0' |--{eth0}{physical_address} = '00:50:56:08:00:f8' |--%{eth1} |--%{eth1}{ip_address} |--{eth1}{ip_address}{152.1.14.200} = '255.255.255.0' |--{eth1}{name} = 'eth1' |--{eth1}{physical_address} = '00:50:56:08:00:f9' |--%{eth2} |--%{eth2}{ip_address} |--{eth2}{ip_address}{10.1.2.33} = '255.255.240.0' |--{eth2}{name} = 'eth2' |--{eth2}{physical_address} = '00:0c:29:ba:c1:77' |--%{lo} |--%{lo}{ip_address} |--{lo}{ip_address}{127.0.0.1} = '255.0.0.0' |--{lo}{name} = 'lo' If the $network_type argument is supplied, a hash reference is returned containing only the configuration for the specified interface: |--%{ip_address} |--{ip_address}{10.1.2.33} = '255.255.240.0' |--{name} = 'eth2' |--{physical_address} = '00:0c:29:ba:c1:77' =cut sub get_network_configuration { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } # Check if a 'public' or 'private' network type argument was specified my $network_type = shift; $network_type = lc($network_type) if $network_type; if ($network_type && $network_type !~ /(public|private)/i) { notify($ERRORS{'WARNING'}, 0, "network type argument can only be 'public' or 'private'"); return; } my %network_configuration; # Check if the network configuration has already been retrieved and saved in this object if (!$self->{network_configuration}) { # Run ipconfig my $command = "ifconfig -a"; my ($exit_status, $output) = $self->execute($command,1); if (!defined($output)) { notify($ERRORS{'WARNING'}, 0, "failed to run command to retrieve network configuration: $command"); return; } # Loop through the ifconfig output lines my $interface_name; for my $line (@$output) { # Extract the interface name from the "flags" line: # en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 if ($line =~ /([^\s:]+).*flags/) { $interface_name = $1; } # Skip to the next line if the interface name has not been determined yet next if !$interface_name; # Parse the "ether" line: # ether 00:0c:29:e0:2c:6f if ($line =~ /ether\s+([\w:]+)/) { $network_configuration{$interface_name}{name} = $interface_name; $network_configuration{$interface_name}{physical_address} = lc($1); } # Parse the IP address line: # inet 137.151.131.151 netmask 0xfffff000 broadcast 137.151.143.255 # converting from hex - nasty if ($line =~ /inet ([\d\.]+) netmask 0x([0123456789abcdef]+) broadcast/) { $network_configuration{$interface_name}{ip_address}{$1} = hex(substr($2,0,2)).".".hex(substr($2,2,2)).".".hex(substr($2,4,2)).".".hex(substr($2,6,2)); } } $self->{network_configuration} = \%network_configuration; notify($ERRORS{'DEBUG'}, 0, "retrieved network configuration:\n" . format_data(\%network_configuration)); } else { notify($ERRORS{'DEBUG'}, 0, "network configuration has already been retrieved"); %network_configuration = %{$self->{network_configuration}}; } # 'public' or 'private' wasn't specified, return all network interface information if (!$network_type) { return \%network_configuration; } # Determine either the private or public interface name based on the $network_type argument my $interface_name; if ($network_type =~ /private/i) { $interface_name = $self->get_private_interface_name(); } else { $interface_name = $self->get_public_interface_name(); } if (!$interface_name) { notify($ERRORS{'WARNING'}, 0, "failed to determine the $network_type interface name"); return; } # Extract the network configuration specific to the public or private interface my $return_network_configuration = $network_configuration{$interface_name}; if (!$return_network_configuration) { notify($ERRORS{'WARNING'}, 0, "network configuration does not exist for interface: $interface_name, network configuration:\n" . format_data(\%network_configuration)); return; } notify($ERRORS{'DEBUG'}, 0, "returning $network_type network configuration"); return $return_network_configuration; } #////////////////////////////////////////////////////////////////////////////// =head2 set_post_load_status Parameters : none Returns : boolean Description : Adds a line to currentimage.txt indicating the vcld OS post_load tasks have run. The format of the line added is: vcld_post_load=success (<time>) This line is checked when a computer is reserved to make sure the post_load tasks have run. A computer may be loaded but the post_load tasks may not run if it is loaded manually or by some other means not controlled by vcld. =cut sub set_post_load_status { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); my $image_os_type = $self->data->get_image_os_type(); my $time = localtime; my $post_load_line = "vcld_post_load=success ($time)"; my $command; # Remove existing lines beginning with vcld_post_load $command = "sed -i '' -e \'/vcld_post_load.*/d\' currentimage.txt"; my ($exit_status, $output) = $self->execute($command, 1); if (defined($exit_status) && $exit_status == 0) { notify($ERRORS{'DEBUG'}, 0, "added line to currentimage.txt on $computer_node_name: '$post_load_line'"); } elsif ($exit_status) { notify($ERRORS{'WARNING'}, 0, "failed to add line to currentimage.txt on $computer_node_name: '$post_load_line', exit status: $exit_status, output:\n" . join("\n", @$output)); return; } else { notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to add line to currentimage.txt on $computer_node_name"); return; } # Add a line to the end of currentimage.txt $command = "echo \"$post_load_line\" >> currentimage.txt"; ($exit_status, $output) = $self->execute($command, 1); if (defined($exit_status) && $exit_status == 0) { notify($ERRORS{'DEBUG'}, 0, "added line to currentimage.txt on $computer_node_name: '$post_load_line'"); } elsif ($exit_status) { notify($ERRORS{'WARNING'}, 0, "failed to add line to currentimage.txt on $computer_node_name: '$post_load_line', exit status: $exit_status, output:\n" . join("\n", @$output)); return; } else { notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to add line to currentimage.txt on $computer_node_name"); return; } # Remove blank lines $command .= " && sed -i '' -e \'/^[\\s\\r\\n]*\$/d\' currentimage.txt"; ($exit_status, $output) = $self->execute($command, 1); if (defined($exit_status) && $exit_status == 0) { notify($ERRORS{'DEBUG'}, 0, "added line to currentimage.txt on $computer_node_name: '$post_load_line'"); } elsif ($exit_status) { notify($ERRORS{'WARNING'}, 0, "failed to add line to currentimage.txt on $computer_node_name: '$post_load_line', exit status: $exit_status, output:\n" . join("\n", @$output)); return; } else { notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to add line to currentimage.txt on $computer_node_name"); return; } return 1; } #////////////////////////////////////////////////////////////////////////////// =head2 get_public_ip_address Parameters : none Returns : string Description : Returns the public IP address assigned to the computer. =cut sub get_public_ip_address { my $self = shift; if (ref($self) !~ /VCL::Module/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $public_network_configuration = $self->get_network_configuration('public'); if (!$public_network_configuration) { notify($ERRORS{'WARNING'}, 0, "failed to retrieve public network configuration"); return; } my $public_ip_address = (keys %{$public_network_configuration->{ip_address}})[0]; if (!$public_ip_address) { notify($ERRORS{'WARNING'}, 0, "'ip_address' key is not set in the public network configuration hash:\n" . format_data($public_network_configuration)); return; } notify($ERRORS{'DEBUG'}, 0, "retrieved public IP address: $public_ip_address"); return $public_ip_address; } #////////////////////////////////////////////////////////////////////////////// =head2 add_user Parameters : Returns : Description : # reserve =cut sub add_user { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return 0; } my $reservation_password = $self->data->get_reservation_password(); # Make sure the user login ID was passed my $user_login_id = shift; $user_login_id = $self->data->get_user_login_id() if (!$user_login_id); if (!$user_login_id) { notify($ERRORS{'WARNING'}, 0, "user could not be determined"); return 0; } # Make sure the computer node was passed my $computer_node_name = shift; $computer_node_name = $self->data->get_computer_node_name() if (!$computer_node_name); if (!$computer_node_name) { notify($ERRORS{'WARNING'}, 0, "computer node name could not be determined"); return 0; } my $useradd_cmd = $self->get_node_configuration_directory() . "/useradd $user_login_id $reservation_password"; if ($self->execute($useradd_cmd,1)) { notify($ERRORS{'DEBUG'}, 0, "added user: $user_login_id to $computer_node_name"); } else { notify($ERRORS{'DEBUG'}, 0, "failed to add user: $user_login_id to $computer_node_name"); } return 1; } ## end sub add_user #////////////////////////////////////////////////////////////////////////////// =head2 firewall_enable Parameters : optional persistence flag Returns : 1 if succeeded, 0 otherwise Description : # pre_capture =cut sub firewall_enable { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); my $persist = shift; my $fw_enable_cmd = ""; if ($persist) { $fw_enable_cmd = $self->get_node_configuration_directory() . "/fw_enable $persist"; } else { $fw_enable_cmd = $self->get_node_configuration_directory() . "/fw_enable"; } if ($self->execute($fw_enable_cmd,1)) { notify($ERRORS{'DEBUG'}, 0, "enabled firewall on $computer_node_name"); } else { notify($ERRORS{'DEBUG'}, 0, "failed to enable firewall on $computer_node_name"); } return 1; } ## end sub firewall_enable #////////////////////////////////////////////////////////////////////////////// =head2 activate_irapp Parameters : None Returns : If successful: true If failed: false Description : Activates iRAPP license =cut sub activate_irapp { my $self = shift; if (ref($self) !~ /osx/i) { notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method"); return; } my $computer_node_name = $self->data->get_computer_node_name(); my $command = '/System/Library/CoreServices/rapserver.app/Contents/Tools/rapliccmd load -q -r -f /var/root/VCL/license.lic'; my ($exit_status, $output) = $self->execute($command,1); if (defined $exit_status && $exit_status == 0) { notify($ERRORS{'DEBUG'}, 0, "executed command to load iRAPP license on $computer_node_name"); } else { notify($ERRORS{'WARNING'}, 0, "failed to run command to load iRAPP license on $computer_node_name, output:\n" . join("\n", @$output)); } return; } #////////////////////////////////////////////////////////////////////////////// 1; __END__