diff --git a/ocf/alteeve/server b/ocf/alteeve/server index 601b9af0..fb848df0 100755 --- a/ocf/alteeve/server +++ b/ocf/alteeve/server @@ -100,6 +100,7 @@ my $conf = { definition => "/mnt/anvil/definitions/#!NAME!#.xml", }, exe => { + brctl => "/usr/sbin/brctl", cibadmin => "/usr/sbin/cibadmin", crm_error => "/usr/sbin/crm_error", drbdadm => "/usr/sbin/drbdadm", @@ -261,137 +262,8 @@ sub start_server # 7. Make sure all bridges exist and soft error if not. # 8. Start the server. - my $server = $conf->{environment}{OCF_RESKEY_name}; - my ($server_xml, $definition_file) = read_server_definition($conf); - #print Dumper $server_xml->{devices}; - - # Does the internal server name match? - if ($server ne $server_xml->{name}->[0]) - { - to_log($conf, {message => "The configured server name: [$server] does not match the name of the server in the definition file: [".$server_xml->{name}."]!", 'line' => __LINE__, level => 0, priority => "err"}); - exit(1); - } - - ### Check that we have enough RAM. - # How mcuh RAM does the server need? - my $server_ram_value = $server_xml->{memory}->[0]->{content}; - my $server_ram_units = $server_xml->{memory}->[0]->{unit}; - to_log($conf, {message => "server_ram_value: [$server_ram_value], server_ram_units: [$server_ram_units].", 'line' => __LINE__, level => 2}); - - # Convert to bytes - my $server_ram_bytes = $server_ram_value; - if ($server_ram_units =~ /^k/i) { $server_ram_bytes = ($server_ram_value * (2 ** 10)); } - elsif ($server_ram_units =~ /^m/i) { $server_ram_bytes = ($server_ram_value * (2 ** 20)); } - elsif ($server_ram_units =~ /^g/i) { $server_ram_bytes = ($server_ram_value * (2 ** 30)); } - elsif ($server_ram_units =~ /^t/i) { $server_ram_bytes = ($server_ram_value * (2 ** 40)); } - elsif ($server_ram_units =~ /^p/i) { $server_ram_bytes = Math::BigInt->new('2')->bpow('50')->bmul($server_ram_value); } - elsif ($server_ram_units =~ /^e/i) { $server_ram_bytes = Math::BigInt->new('2')->bpow('60')->bmul($server_ram_value); } - elsif ($server_ram_units =~ /^z/i) { $server_ram_bytes = Math::BigInt->new('2')->bpow('70')->bmul($server_ram_value); } - elsif ($server_ram_units =~ /^y/i) { $server_ram_bytes = Math::BigInt->new('2')->bpow('80')->bmul($server_ram_value); } - to_log($conf, {message => "server_ram_bytes: [$server_ram_bytes].", 'line' => __LINE__, level => 3}); - - # How much RAM do we have available? - my $available = 0; - my ($free_rc, $free_output) = shell_call($conf, $conf->{path}{exe}{free}." --bytes"); - foreach my $line (split/\n/, $free_output) - { - to_log($conf, {message => "line: [$line].", 'line' => __LINE__, level => 3}); - if ($line =~ /Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/) - { - my $total = $1; - my $used = $2; - my $free = $3; - my $shared = $4; - my $cache = $5; - $available = $6; - to_log($conf, {message => "total: [$total], used: [$used], free: [$free], shared: [$shared], cache: [$cache], available: [$available]", 'line' => __LINE__, level => 3}); - } - } - - to_log($conf, {message => "server_ram_bytes: [".comma($conf, $server_ram_bytes)." bytes].", 'line' => __LINE__, level => 2}); - to_log($conf, {message => "available: ...... [".comma($conf, $available)." bytes].", 'line' => __LINE__, level => 2}); - if ($server_ram_bytes > $available) - { - # Not enough free memory. - to_log($conf, {message => "The configured server name: [$server] needs: [".comma($conf, $server_ram_bytes)." bytes] of RAM, but only: [".comma($conf, $available)." bytes] are available!", 'line' => __LINE__, level => 0, priority => "err"}); - exit(1); - } - - # What emulator is this using? - my $emulator = $server_xml->{devices}->[0]->{emulator}->[0]; - to_log($conf, {message => "emulator: [$emulator]", 'line' => __LINE__, level => 2}); - if (not -e $emulator) - { - # It doesn't exist. Exit with OCF_ERR_INSTALLED (5). - to_log($conf, {message => "The server wants to use the emulator: [$emulator] which doesn't exist on this node. Was this server migrated from a different generation Anvil! system? Please update '...' in the server's definition file: [$definition_file].", 'line' => __LINE__, level => 0, priority => "err"}); - exit(5); - } - if (not -x $emulator) - { - # We can't execute it. Exit with OCF_ERR_PERM (4). - to_log($conf, {message => "The server wants to use the emulator: [$emulator] which exists, but we can't run. Please check permissions and for SELinux denials.", 'line' => __LINE__, level => 0, priority => "err"}); - exit(4); - } - - # Find the Optical drives and DRBD devices. - foreach my $device_ref (@{$server_xml->{devices}}) - { - foreach my $interface_ref (@{$device_ref->{interface}}) - { - foreach my $source_ref (@{$interface_ref->{source}}) - { - my $bridge = $source_ref->{bridge}; - $conf->{bridges}{$bridge} = 1; - to_log($conf, {message => "bridges::${bridge}: [".$conf->{bridges}{$bridge}."].", 'line' => __LINE__, level => 2}); - } - } - } - - # Find the bridge(s) this server uses. - foreach my $device_ref (@{$server_xml->{devices}}) - { - foreach my $disk_ref (@{$device_ref->{disk}}) - { - my $type = $disk_ref->{device}; - to_log($conf, {message => "type: [$type].", 'line' => __LINE__, level => 2}); - if ($type eq "disk") - { - foreach my $source_ref (@{$disk_ref->{source}}) - { - my $disk = $source_ref->{dev}; - $conf->{disks}{$disk} = 1; - to_log($conf, {message => "disks::${disk}: [".$conf->{disks}{$disk}."].", 'line' => __LINE__, level => 2}); - } - } - elsif ($type eq "cdrom") - { - foreach my $source_ref (@{$disk_ref->{source}}) - { - my $file = $source_ref->{file}; - $conf->{optical}{$file} = 1; - to_log($conf, {message => "optical::${file}: [".$conf->{optical}{$file}."].", 'line' => __LINE__, level => 2}); - } - } - } - } - - # Verify DRBD devices now - foreach my $disk (sort {$a cmp $b} keys %{$conf->{disks}}) - { - to_log($conf, {message => "Checking that the DRBD device: [$disk] is ready.", 'line' => __LINE__, level => 2}); - } - - # Verify optical disks now - foreach my $file (sort {$a cmp $b} keys %{$conf->{optical}}) - { - to_log($conf, {message => "Checking that the optical disc image: [$file] exists.", 'line' => __LINE__, level => 2}); - } - - # Verify bridges now - foreach my $bridge (sort {$a cmp $b} keys %{$conf->{bridges}}) - { - to_log($conf, {message => "Checking that we have a bridge called: [$bridge].", 'line' => __LINE__, level => 2}); - } + to_log($conf, {message => "We've been asked to start the server: [".$conf->{environment}{OCF_RESKEY_name}."]..", 'line' => __LINE__, level => 2}); + validate_all($conf); exit(0); } @@ -549,19 +421,251 @@ sub migrate_server } # Validation checks that we have the definition XML, resource config and that needed apps are installed. -sub validate +sub validate_all { my ($conf) = @_; + to_log($conf, {message => "Running validation tests...", 'line' => __LINE__, level => 2}); + + # Read in the server's definition file (if found and readable). + read_server_definition($conf); + to_log($conf, {message => "- Server definition was read.", 'line' => __LINE__, level => 2}); + + # Does the internal server name match? + validate_name($conf); + to_log($conf, {message => "- Server name is valid.", 'line' => __LINE__, level => 2}); + + # Make sure the emulator it wants is the one we have. + validate_emulator($conf); + to_log($conf, {message => "- Eumlator is valid.", 'line' => __LINE__, level => 2}); + + # These tests are only needed if we're about to boot the server + if ($conf->{switches}{start}) + { + # Check that we have enough RAM. + validate_ram($conf); + to_log($conf, {message => "- Sufficient RAM is available.", 'line' => __LINE__, level => 2}); + } - ### Exit options; - # OCF_SUCCESS (0) - all is well. - # OCF_ERR_CONFIGURED (6) - the user has misconfigured the resource (the server name doesn't exist). - # OCF_ERR_INSTALLED  (5) - The resource has possibly been configured correctly, but a vital component is missing on the node where validate-all is being executed. - # OCF_ERR_PERM  (4) - the resource is configured correctly and is not missing any required components, but is suffering from a permission issue (such as not being able to create a necessary file). + # Validate storage (Disks and optical media) + validate_storage($conf); + to_log($conf, {message => "- Storage is valid and ready.", 'line' => __LINE__, level => 2}); + + # Validate bridges + validate_bridges($conf); + to_log($conf, {message => "- Network bridge(s) are available.", 'line' => __LINE__, level => 2}); exit(0); } +# This ensures that the bridges the server connects to exist on this node. +sub validate_bridges +{ + my ($conf) = @_; + + # Find the Optical drives and DRBD devices. + foreach my $device_ref (@{$conf->{server}{definition_xml}->{devices}}) + { + foreach my $interface_ref (@{$device_ref->{interface}}) + { + foreach my $source_ref (@{$interface_ref->{source}}) + { + my $bridge = $source_ref->{bridge}; + $conf->{server}{bridges}{$bridge} = 1; + to_log($conf, {message => "server::bridges::${bridge}: [".$conf->{server}{bridges}{$bridge}."].", 'line' => __LINE__, level => 3}); + } + } + } + + # Get a list of available bridges. + my ($return_code, $output) = shell_call($conf, $conf->{path}{exe}{brctl}." show"); + my $first_line = 1; + foreach my $line (split/\n/, $output) + { + to_log($conf, {message => "line: [$line].", 'line' => __LINE__, level => 3}); + if ($first_line) + { + # We skip the first line instead of parse the string to avoid getting caught by + # translations. + $first_line = 0; + } + elsif ($line =~ /^(\S+)\s/) + { + my $bridge = $1; + $conf->{'local'}{bridge}{$bridge} = 1; + to_log($conf, {message => "local::bridge::${bridge}: [$bridge].", 'line' => __LINE__, level => 3}); + } + } + + # Verify bridges now + foreach my $bridge (sort {$a cmp $b} keys %{$conf->{server}{bridges}}) + { + if ($conf->{'local'}{bridge}{$bridge}) + { + to_log($conf, {message => "The bridge: [$bridge] is available for this server.", 'line' => __LINE__, level => 2}); + } + else + { + # Missing bridge. + to_log($conf, {message => "The server wants to connect to the bridge: [$bridge] which we do not have on this node.", 'line' => __LINE__, level => 0, priority => "err"}); + exit(5); + } + } + + return(0); +} + +# This looks up the disks and optical media connected to this server. +sub validate_storage +{ + my ($conf) = @_; + + # Find the bridge(s) this server uses. + foreach my $device_ref (@{$conf->{server}{definition_xml}->{devices}}) + { + foreach my $disk_ref (@{$device_ref->{disk}}) + { + my $type = $disk_ref->{device}; + to_log($conf, {message => "type: [$type].", 'line' => __LINE__, level => 2}); + if ($type eq "disk") + { + foreach my $source_ref (@{$disk_ref->{source}}) + { + my $disk = $source_ref->{dev}; + $conf->{server}{disks}{$disk} = 1; + to_log($conf, {message => "server::disks::${disk}: [".$conf->{server}{disks}{$disk}."].", 'line' => __LINE__, level => 2}); + } + } + elsif ($type eq "cdrom") + { + foreach my $source_ref (@{$disk_ref->{source}}) + { + my $file = $source_ref->{file}; + $conf->{server}{optical}{$file} = 1; + to_log($conf, {message => "server::optical::${file}: [".$conf->{server}{optical}{$file}."].", 'line' => __LINE__, level => 2}); + } + } + } + } + + # Verify DRBD devices now + foreach my $disk (sort {$a cmp $b} keys %{$conf->{server}{disks}}) + { + to_log($conf, {message => "Checking that the DRBD device: [$disk] is ready.", 'line' => __LINE__, level => 2}); + } + + # Verify optical disks now + foreach my $file (sort {$a cmp $b} keys %{$conf->{server}{optical}}) + { + to_log($conf, {message => "Checking that the optical disc image: [$file] exists.", 'line' => __LINE__, level => 2}); + + # If the file doesn't exist, exit with OCF_ERR_INSTALLED (5). If we can't read it, exit with + # OCF_ERR_PERM (4). + if (not -e $file) + { + } + elsif (not -r $file) + { + } + else + { + # We're OK. + } + } + + return(0); +} + +# This verifies that the requested emulator exists and can be used. +sub validate_emulator +{ + my ($conf) = @_; + + # What emulator is this using? + my $emulator = $conf->{server}{definition_xml}->{devices}->[0]->{emulator}->[0]; + to_log($conf, {message => "emulator: [$emulator]", 'line' => __LINE__, level => 2}); + if (not -e $emulator) + { + # It doesn't exist. Exit with OCF_ERR_INSTALLED (5). + to_log($conf, {message => "The server wants to use the emulator: [$emulator] which doesn't exist on this node. Was this server migrated from a different generation Anvil! system? Please update '...' in the server's definition file: [".$conf->{server}{definition_file}."].", 'line' => __LINE__, level => 0, priority => "err"}); + exit(5); + } + if (not -x $emulator) + { + # We can't execute it. Exit with OCF_ERR_PERM (4). + to_log($conf, {message => "The server wants to use the emulator: [$emulator] which exists, but we can't run. Please check permissions and for SELinux denials.", 'line' => __LINE__, level => 0, priority => "err"}); + exit(4); + } + + return(0); +} + +# This makes sure the name we see in the definition file matches what we expect. +sub validate_name +{ + my ($conf) = @_; + + my $server = $conf->{environment}{OCF_RESKEY_name}; + if ($server ne $conf->{server}{definition_xml}->{name}->[0]) + { + to_log($conf, {message => "The configured server name: [$server] does not match the name of the server in the definition file: [".$conf->{server}{definition_xml}->{name}."]!", 'line' => __LINE__, level => 0, priority => "err"}); + exit(1); + } + + return(0); +} + +# This checks that there is enough RAM to run this server. +sub validate_ram +{ + my ($conf) = @_; + + # How mcuh RAM does the server need? + my $server_ram_value = $conf->{server}{definition_xml}->{memory}->[0]->{content}; + my $server_ram_units = $conf->{server}{definition_xml}->{memory}->[0]->{unit}; + to_log($conf, {message => "server_ram_value: [$server_ram_value], server_ram_units: [$server_ram_units].", 'line' => __LINE__, level => 2}); + + # Convert to bytes + my $server_ram_bytes = $server_ram_value; + if ($server_ram_units =~ /^k/i) { $server_ram_bytes = ($server_ram_value * (2 ** 10)); } + elsif ($server_ram_units =~ /^m/i) { $server_ram_bytes = ($server_ram_value * (2 ** 20)); } + elsif ($server_ram_units =~ /^g/i) { $server_ram_bytes = ($server_ram_value * (2 ** 30)); } + elsif ($server_ram_units =~ /^t/i) { $server_ram_bytes = ($server_ram_value * (2 ** 40)); } + elsif ($server_ram_units =~ /^p/i) { $server_ram_bytes = Math::BigInt->new('2')->bpow('50')->bmul($server_ram_value); } + elsif ($server_ram_units =~ /^e/i) { $server_ram_bytes = Math::BigInt->new('2')->bpow('60')->bmul($server_ram_value); } + elsif ($server_ram_units =~ /^z/i) { $server_ram_bytes = Math::BigInt->new('2')->bpow('70')->bmul($server_ram_value); } + elsif ($server_ram_units =~ /^y/i) { $server_ram_bytes = Math::BigInt->new('2')->bpow('80')->bmul($server_ram_value); } + to_log($conf, {message => "server_ram_bytes: [$server_ram_bytes].", 'line' => __LINE__, level => 3}); + + # How much RAM do we have available? + my $available = 0; + my ($free_rc, $free_output) = shell_call($conf, $conf->{path}{exe}{free}." --bytes"); + foreach my $line (split/\n/, $free_output) + { + to_log($conf, {message => "line: [$line].", 'line' => __LINE__, level => 3}); + if ($line =~ /Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/) + { + my $total = $1; + my $used = $2; + my $free = $3; + my $shared = $4; + my $cache = $5; + $available = $6; + to_log($conf, {message => "total: [$total], used: [$used], free: [$free], shared: [$shared], cache: [$cache], available: [$available]", 'line' => __LINE__, level => 3}); + } + } + + to_log($conf, {message => "server_ram_bytes: [".comma($conf, $server_ram_bytes)." bytes].", 'line' => __LINE__, level => 2}); + to_log($conf, {message => "available: ...... [".comma($conf, $available)." bytes].", 'line' => __LINE__, level => 2}); + if ($server_ram_bytes > $available) + { + # Not enough free memory. + to_log($conf, {message => "The configured server name: [".$conf->{environment}{OCF_RESKEY_name}."] needs: [".comma($conf, $server_ram_bytes)." bytes] of RAM, but only: [".comma($conf, $available)." bytes] are available!", 'line' => __LINE__, level => 0, priority => "err"}); + exit(1); + } + + return(0); +} + # This reads the XML definition data into an XML data hash. sub read_server_definition { @@ -603,7 +707,10 @@ sub read_server_definition exit(1); } - return($server_xml, $definition_file); + $conf->{server}{definition_xml} = $server_xml; + $conf->{server}{definition_file} = $definition_file; + + return(0); } # This reads in a file and returns the contents as a single string variable.