blob: b58e81df7ed3bf97840af82429b8c543dd4b33bd [file] [log] [blame]
#!/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__