Author: arkurth
Date: Mon Jul 18 22:34:21 2016
New Revision: 1753324

URL: http://svn.apache.org/viewvc?rev=1753324&view=rev
Log:
VCL-961
Added subroutines to Linux.pm:
get_ifcfg_file_info
get_network_bridge_info

Rewrote Linux.pm::enable_dhcp. It now checks if an interface is bridged and 
does not overwrite bridge or most other custom configurations. It selectively 
removes certain parameters which are specific to a particular network or 
computer.

Added ifcfg-* backup files to $CAPTURE_DELETE_FILE_PATHS to prevent backups 
from accumulating. When an image is captured, a single backup should remain 
since enable_dhcp is called after clean_known_files.

Modified:
    vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm?rev=1753324&r1=1753323&r2=1753324&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux.pm Mon Jul 18 22:34:21 2016
@@ -102,6 +102,7 @@ our $CAPTURE_DELETE_FILE_PATHS = [
        '/root/.ssh/id_rsa.pub',
        '/etc/sysconfig/iptables*old*',
        '/etc/sysconfig/iptables_pre*',
+       '/etc/sysconfig/network-scripts/ifcfg-*.20*-*',
        '/etc/udev/rules.d/70-persistent-net.rules',
        '/var/log/*.0',
        '/var/log/*.gz',
@@ -406,8 +407,12 @@ sub pre_capture {
        }
        
        # Configure the private and public interfaces to use DHCP
-       if (!$self->enable_dhcp()) {
-               notify($ERRORS{'WARNING'}, 0, "failed to enable DHCP on the 
public and private interfaces");
+       if (!$self->enable_dhcp($self->get_private_interface_name())) {
+               notify($ERRORS{'WARNING'}, 0, "failed to enable DHCP on the 
private interface");
+               return;
+       }
+       if (!$self->enable_dhcp($self->get_public_interface_name())) {
+               notify($ERRORS{'WARNING'}, 0, "failed to enable DHCP on the 
public interface");
                return;
        }
        
@@ -3287,55 +3292,218 @@ sub enable_dhcp {
        
        my $computer_node_name = $self->data->get_computer_node_name();
        
-       my $interface_name_argument = shift;
-       my @interface_names;
-       if (!$interface_name_argument) {
-               push(@interface_names, $self->get_private_interface_name());
-               push(@interface_names, $self->get_public_interface_name());
+       my $interface_name = shift;
+       if (!$interface_name) {
+               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
+               return;
        }
-       elsif ($interface_name_argument =~ /private/i) {
-               push(@interface_names, $self->get_private_interface_name());
+       
+       my $calling_subroutine = get_calling_subroutine();
+       
+       my $ifcfg_directory_path = "/etc/sysconfig/network-scripts";
+       my $ifcfg_file_name = "ifcfg-$interface_name";
+       my $ifcfg_file_path = "$ifcfg_directory_path/$ifcfg_file_name";
+       
+       if ($self->file_exists($ifcfg_file_path)) {
+               my $timestamp = POSIX::strftime("%Y-%m-%d_%H-%M-%S\n", 
localtime);
+               my $ifcfg_backup_file_path = 
"$ifcfg_directory_path/$ifcfg_file_name.$timestamp";
+               $self->copy_file($ifcfg_file_path, $ifcfg_backup_file_path);
        }
-       elsif ($interface_name_argument =~ /public/i) {
-               push(@interface_names, $self->get_public_interface_name());
+       
+       my $ifcfg_file_info = $self->get_ifcfg_file_info($interface_name) || {};
+       
+       if ($calling_subroutine !~ /enable_dhcp/) {
+               # Check if interface is configured as a bridge
+               my @bridge_interface_names;
+               if ($ifcfg_file_info->{BRIDGE}) {
+                       push @bridge_interface_names, 
$ifcfg_file_info->{BRIDGE};
+               }
+               elsif ($ifcfg_file_info->{TYPE} && $ifcfg_file_info->{TYPE} =~ 
/Bridge/i) {
+                       # For ifcfg-br* files, the name of the physical 
interface usually isn't listed in the file
+                       # Get the network bridge info
+                       my $network_bridge_info = 
$self->get_network_bridge_info();
+                       if (defined($network_bridge_info) && 
defined($network_bridge_info->{$interface_name})) {
+                               @bridge_interface_names = 
@{$network_bridge_info->{$interface_name}{interfaces}};
+                       }
+               }
+               for my $bridge_interface_name (@bridge_interface_names) {
+                       # Make sure the bridge isn't the same name as the 
interface being checked to avoid recurive loop
+                       next if ($bridge_interface_name eq $interface_name);
+                       
+                       notify($ERRORS{'DEBUG'}, 0, "$interface_name is 
bridged, attempting to enable DHCP on bridge interface: 
$bridge_interface_name");
+                       $self->enable_dhcp($bridge_interface_name) || return;
+               }
        }
-       else {
-               push(@interface_names, $interface_name_argument);
+       
+       # Add/overwrite required parameters to file contents
+       my $set_parameters = {
+               'BOOTPROTO' => 'dhcp',
+               'DEVICE' => $interface_name,
+               'NAME' => $interface_name,
+               'ONBOOT' => 'yes',
+       };
+       for my $parameter (keys %$set_parameters) {
+               my $value = $set_parameters->{$parameter};
+               $ifcfg_file_info->{$parameter} = $value;
+       }
+       
+       # Remove parameters which are specific to a particular network or 
computer
+       my @remove_parameter_patterns = (
+               'ADDR',
+               'BROADCAST',
+               'DNS',
+               'GATEWAY',
+               'HOSTNAME',
+               'METRIC',
+               'NETMASK',
+               'NETWORK',
+               'PREFIX',
+               'UUID',
+       );
+       for my $remove_pattern (@remove_parameter_patterns) {
+               my @matching_properties = grep { $_ =~ /.*$remove_pattern.*/ } 
sort keys %$ifcfg_file_info;
+               if (@matching_properties) {
+                       notify($ERRORS{'DEBUG'}, 0, "removing parameters from 
ifcfg-$interface_name file matching pattern '$remove_pattern': " . join(', ', 
@matching_properties));
+                       map { delete $ifcfg_file_info->{$_} } 
@matching_properties;
+               }
        }
        
-       for my $interface_name (@interface_names) {
-               my $ifcfg_file_path = 
"/etc/sysconfig/network-scripts/ifcfg-$interface_name";
-               notify($ERRORS{'DEBUG'}, 0, "attempting to enable DHCP on 
interface: $interface_name\nifcfg file path: $ifcfg_file_path");
-               
-               my $ifcfg_file_contents = <<EOF;
-DEVICE=$interface_name
-BOOTPROTO=dhcp
-ONBOOT=yes
-EOF
-               
-               # Remove any Windows carriage returns
-               $ifcfg_file_contents =~ s/\r//g;
-               
-               # Remove the last newline
-               $ifcfg_file_contents =~ s/\n$//s;
-               
-               # Write the contents to the ifcfg file
-               if ($self->create_text_file($ifcfg_file_path, 
$ifcfg_file_contents)) {
-                       notify($ERRORS{'DEBUG'}, 0, "updated 
$ifcfg_file_path:\n" . string_to_ascii($ifcfg_file_contents));
+       # Convert the parameter/value hash to a string
+       my $updated_ifcfg_contents;
+       for my $parameter (sort keys %$ifcfg_file_info) {
+               my $value = $ifcfg_file_info->{$parameter};
+               $updated_ifcfg_contents .= "$parameter=$value\n";
+       }
+       
+       # Create the text file
+       notify($ERRORS{'DEBUG'}, 0, "updated ifcfg-$interface_name 
contents:\n$updated_ifcfg_contents");
+       return $self->create_text_file($ifcfg_file_path, 
$updated_ifcfg_contents);
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+=head2 get_ifcfg_file_info
+
+ Parameters  : $interface_name
+ Returns     : hash reference
+ Description : Parses the file:
+               /etc/sysconfig/network-scripts/ifcfg-<interface name>
+               
+               A hash is constructed such as:
+               {
+                 "BOOTPROTO" => "dhcp",
+                 "DEVICE" => "eth0",
+                 "ONBOOT" => "yes"
+               }
+               
+               The hash key names are guaranteed to be uppercase.
+
+=cut
+
+sub get_ifcfg_file_info {
+       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 $interface_name = shift;
+       if (!$interface_name) {
+               notify($ERRORS{'WARNING'}, 0, "interface name argument was not 
supplied");
+               return;
+       }
+       
+       my $ifcfg_file_path = 
"/etc/sysconfig/network-scripts/ifcfg-$interface_name";
+       
+       my $info = {};
+       my @lines = $self->get_file_contents($ifcfg_file_path);
+       for my $line (@lines) {
+               next if $line =~ /^\s*#/;
+               my ($property, $value) = $line =~ /^\s*([^=]+)\s*=\s*(.*)\s*$/g;
+               if (defined($property)) {
+                       $info->{uc($property)} = $value;
                }
                else {
-                       notify($ERRORS{'WARNING'}, 0, "failed to update 
$ifcfg_file_path");
-                       return;
+                       notify($ERRORS{'WARNING'}, 0, "failed to parse line 
from $ifcfg_file_path: '$line'");
+               }
+       }
+       
+       notify($ERRORS{'DEBUG'}, 0, "parsed $ifcfg_file_path:\n" . 
format_data($info));
+       return $info;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+=head2 get_network_bridge_info
+
+ Parameters  : none
+ Returns     : hash reference
+ Description : Executes 'brctl show' and parses the output. A hash is
+               constructed:
+               {
+                 "br1" => {
+                   "bridge_id" => "",
+                   "interfaces" => [
+                     "eth1",
+                     "eth2"
+                   ],
+                   "stp_enabled" => ""
+                 }
+               }
+
+=cut
+
+sub get_network_bridge_info {
+       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_name = $self->data->get_computer_short_name();
+       
+       # It's possible that a bridge will have multiple interfaces:
+       # [root@bn19-183 network-scripts]# brctl show
+       # bridge name     bridge id               STP enabled     interfaces
+       # br1             8000.000c29494c97       no              eth1
+       #                                                         eth2
+       
+       my $command = "brctl show";
+       my ($exit_status, $output) = $self->execute($command);
+       if (!defined($output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to execute command on 
$computer_name: $command");
+               return;
+       }
+       elsif ($exit_status ne '0') {
+               notify($ERRORS{'WARNING'}, 0, "failed to retrieve network 
bridge configuration from $computer_name, exit status: $exit_status, 
command:\n$command\noutput:\n" . join("\n", @$output));
+               return 0;
+       }
+       
+       my $network_bridge_info = {};
+       my $current_bridge_name;
+       for my $line (@$output) {
+               my ($bridge_name, $bridge_id, $stp_enabled, $interface_name) = 
$line =~ /^([^\s]*)\s+([^\s]*)\s+([^\s]*)\s+([^\s]+)\s*$/g;
+               if (!defined($interface_name)) {
+                       notify($ERRORS{'DEBUG'}, 0, "ignoring line, interface 
name was not found: '$line'");
+                       next;
                }
                
-               # Remove any leftover ifcfg-*.bak files
-               
$self->delete_file('/etc/sysconfig/network-scripts/ifcfg-eth*.bak');
+               if ($bridge_name) {
+                       $current_bridge_name = $bridge_name;
+               }
+               elsif (!$current_bridge_name) {
+                       notify($ERRORS{'WARNING'}, 0, "failed to retrieve 
network bridge configuration from $computer_name, bridge name unknown\n" .
+                               "line: '$line'\n" .
+                               "output:\n" . join("\n", @$output)
+                       );
+                       return;
+               }
                
-               # Remove dhclient lease files
-               $self->delete_file('/var/lib/dhclient/*.leases');
+               $network_bridge_info->{$current_bridge_name}{bridge_id} = 
$bridge_id if defined($bridge_id);
+               $network_bridge_info->{$current_bridge_name}{stp_enabled} = 
$bridge_id if defined($stp_enabled);
+               push 
@{$network_bridge_info->{$current_bridge_name}{interfaces}}, $interface_name if 
defined($interface_name);
        }
        
-       return 1;
+       notify($ERRORS{'OK'}, 0, "retrieved network bridge configuration from 
$computer_name:" . format_data($network_bridge_info));
+       return $network_bridge_info;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
@@ -6660,6 +6828,9 @@ sub mount_nfs_share {
                #    dmesg | tail  or so
                if (!$self->command_exists('mount.nfs')) {
                        $self->install_package('nfs-utils');
+                       
+                       # On Ubuntu:
+                       $self->install_package('nfs-common');
                }
                
                # Check if the rpcbind service exists, if not, try to install it


Reply via email to