Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Im trying to learn hashes and hitting a snag. Here is what i want to do is have server1 and server2 which are both part of cluster1 and server3 and server4 which are part of cluster2. I want to take cpu(using extract command to get my cpu info) gather it from server1 and server2 and then get total by adding them together as a cluster( in the example cluser1). My problem is I want to print like this: cluster 1 cpu avr is 15.00% cluster2 cpu avr is 10.00% How do I gather the info separtely (i have to remsh to each server to get cpu info) and then push them back to know what cluster they belong too. Here is what ive tried.

%servers = ( 'server1' => 'cluser1', 'server2' => 'cluser1', 'server3' => 'cluser2', 'server4' => 'cluser2' ); foreach $key ( keys(%servers)){ push(@(values(%servers)); }
From here i would do the rest of the script. except this fails. sorry for any typo i trying to remember this from memory. any help would be appreciated.

Replies are listed 'Best First'.
Re: clusters with hashes
by dragonchild (Archbishop) on May 05, 2006 at 14:26 UTC
    my %clusters = ( cluster1 => [ 'server1', 'server2' ], cluster2 => [ 'server3', 'server4' ], ); my %averages; while ( my ($cluster, $machines) = each %clusters ) { my @values; foreach my $machine ( @$machines ) { push @values, # Whatever you do here for $machine } $averages{ $cluster } = # Whatever you do here with @values }
    Study that code. It's one proper implemention (of many). It uses many important concepts, such as references and general data structures. Please feel free to ask questions about how it works.

    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
Re: clusters with hashes
by davidrw (Prior) on May 05, 2006 at 14:27 UTC
    I think you want to hash it the other way, going from cluster to servers (big to small):
    use strict; # always use this use warnings; # .. and this my %servers = ( # store the server cpu values by cluster cluster1 => { server1 => 10, server2 => 20, }, cluster2 => { server3 => 15, server4 => 5, }, ); foreach my $clusterName ( keys %servers ){ my $servers = $servers{$clusterName}; next unless scalar values %$servers; # avoid div by zero my $sum = 0; $sum += $_ for values %$servers; printf "%s cpu avg is %.2d%%\n", $clusterName, 100*$sum/(scalar valu +es %$servers); }
Re: clusters with hashes
by Herkum (Parson) on May 05, 2006 at 14:29 UTC

    How about structuring your data like this,

    %usage_for = ( 'cluster1' => { 'server1' => 5, 'server2' => 10, }, 'cluster2' => { 'server3' => 20, 'server4' => 30, } ); foreach my $cluster (keys %usage_for) { my $usage; print "Cluster Servers\n"; foreach my $server (keys %{$usage_for{$cluster}}) { $usage += $usage_for{$cluster}{$server}; print "\t$server => " . $usage_for{$cluster}{$server} . "\n"; } print "Usage for $cluster => " . $usage . "%\n"; }
Re: clusters with hashes
by liverpole (Monsignor) on May 05, 2006 at 14:29 UTC
    I think you want something like this (and note the use of strict and warnings, which should help you a lot when writing program):

    (Update:  Having now seen them, I actually like dragonchild and davidrw's answers, above (as well as Herkum's, below), even better, because they redesigned the data structure to be more natural.  So my answer, though closer to your original code, is not the one I would choose.)

    #!/usr/bin/perl -w + use strict; use warnings; + + my %servers = ( 'server1' => 'cluster1', 'server2' => 'cluster1', 'server3' => 'cluster2', 'server4' => 'cluster2' ); + my %values = ( 'server1' => 20.00, 'server2' => 10.00, 'server3' => 5.00, 'server4' => 15.00, ); + my %cluster_count; + # # Here is where you fill in values, such that... # # $values{'server1'} = <cpu info for server1> # $values{'server2'} = <cpu info for server2> # ... etc ... # # But, I'm going to cheat and do ... + %values = ( 'server1' => 20.00, 'server2' => 10.00, 'server3' => 5.00, 'server4' => 15.00, ); + my %results_by_cluster; + # # Calculate results by cluster, and save cluster counts (this is # necessary for the calculation of averages). # foreach my $srvr (keys (%servers)) { my $cluster = $servers{$srvr}; ++$cluster_count{$cluster}; $results_by_cluster{$cluster} += $values{$srvr}; } + # # Display results # foreach my $cluster (qw(cluster1 cluster2)) { my $avg = $results_by_cluster{$cluster} / $cluster_count{$cluster} +; printf "%20.20s average is: %f\n", $cluster, $avg; }

    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: clusters with hashes
by ptum (Priest) on May 05, 2006 at 14:27 UTC

    This seems like a rather poorly defined problem. Let me try to reconstruct what you are trying to do:

    • You have a number of clusters, for which you want to report total (or possibly average?) CPU usage %.
    • Each cluster has a number of servers, whose names you need to keep track of so you can contact them for CPU info.

    So one way to address this problem is to have a hash keyed by server name, and each hash entry's value is a reference to an array of values (server_name, CPU utilization).

    Try something like this (untested) code to accumulate total CPU usage:

    use strict; use warnings; my %server_hash = ( 'server1' => ['cluster1',0], 'server2' => ['cluster1',0], 'server3' => ['cluster2',0], 'server4' => ['cluster2',0] ); my %cluster_totals = (); foreach my $this_server(keys %server_hash) { $server_hash{$this_server}->[1] = getCPUUtilization($this_server); $cluster_totals{$server_hash{$this_server}->[0]} += $server_hash{$th +is_server}->[1]; } foreach (keys %cluster_totals) { print "Cluster: $_ \t $cluster_totals{$_}\n"; }

    Granted, I'm using an extra hash where I probably don't need to, and this solution is off-the-cuff. But it might provide you with a starting point. The nice thing about a hash of array references is that it is extensible when you need to store yet another attribute of the server.

Re: clusters with hashes
by Anonymous Monk on May 05, 2006 at 14:44 UTC
    The assumption that each cluster consists of exactly two servers is hardcoded into the last line.
    #!/usr/bin/perl -T use strict; use diagnostics; # -- sub get_cpu { my $servername = shift; my %cpu = ( server1 => 10, server2 => 20, server3 => 5, server4 => 15, ); return $cpu{$servername}; }; # -- my %servers = ( server1 => 'cluster1', server2 => 'cluster1', server3 => 'cluster2', server4 => 'cluster2', ); my %clusters; foreach my $servername (keys %servers) { $clusters{$servers{$servername}} += get_cpu($servername); }; print "$_:\t".($clusters{$_}/2)."\n" foreach sort keys %clusters;
      Or skip the hardcoded server count assumption.
      my %clustertotal; my %clustercount; foreach my $servername (keys %servers) { $clustertotal{$servers{$servername}} += get_cpu($servername); $clustercount{$servers{$servername}}++; }; print "$_:\t".($clustertotal{$_}/$clustercount{$_})."\n" foreach sort +keys %clusters;