Edit D:\app\Administrator\product\11.2.0\dbhome_1\sysman\admin\scripts\storage_report_metrics.pl
#!/usr/local/bin/perl # # $Header: emagent/sysman/admin/scripts/storage_report_metrics.pl /st_emagent_10.2.0.4.2db11.2/2 2009/02/24 21:21:10 rajverma Exp $ # # Copyright (c) 2004, 2009, Oracle and/or its affiliates.All rights reserved. # # NAME # storage_report_metrics.pl - <one-line expansion of the name> # # DESCRIPTION # Collect metrics for all storage layers and build the storage layout # analyze the storage layout # instrument the data and keys metrics for the layout # # NOTES # Things to do # # A hash to reach elements in the same order as they were created # # A key_value identifies an entity on the OS eg. DISK # An key_value can have multiple instances on the OS eg. BLOCK AND RAW,PHYSICAL # AND LOGICAL # Each instance of an key_value may have one or more paths on the OS # Two different key_values may refer to the smae physical entity eg. MULTIPATHED # DISKS # key_values for the same physical entity will have the same global_unique_id # global_unique_ids are required in raw metrics only for the lower most entities # in the storage layout , ie for DISKS the global unique id for higher level # metrics will be generated # # ------------------------------------------------------------------------------- # Sequence of execution # This should be kept up to date when changes are mode to code #-------------------------------------------------------------------------------- # # 1. fninit - initialization - prepare logging directories # # execute fngsm if the metric requested is a data metric # 2. fngsm - generate storage metrics and cache them to file # # 2.0 fncshrm - remove the cached metric files # remove the nmhs(data|alias|keys|issues|rmet|fcsh).txt files # from the cached directory if they exist # # 2.1 fncrsm - collect raw storage metrics # -> @alldata # execute raw metric functions get the results as array to @amhrff # push the data in @amhrff to @alldata # # 2.1.1 fndrmtf - dump raw metrics to file on disk # @allldata -> # dump the raw data to a file on disk # 2.1.2 fnvrawd - validate the raw data # @allldata -> @allldata # # 2.1.3 fnprawd - prepare the raw dtaa for further processing # @allldata -> @allldata # strip leading/trailing blanks # append storage layer to key_value, parent_key_value # append storage_layer to parent/child key criteria # remove trailing/leading spaces in data # append storage_layer to kv, pkv # # 2.3 fnblds - build quick look up data structures # @alldata -> # build the kv index %iekv # use the config $config{pfdflds} to pick the values for fields in %iekv # if multiple entities have the same key_value # If a parent key_valus is defined push it to the %iekv index as # {$iekv{kv}}->{pkvs}{parent_key_value}=1 # if the child/parent key criteria are defined push it to the %iekv # index as @{$iekv{kv}->{parent_key_criteria}} # for entities with os_identifier build the %ieosp index # %isosp{key_value}{kv}{osid}=%iekv{kv} # %ieosp{os_identifier}{osid}{kv} = %iekv{kv} # # 2.4 fnpcsm - process the collected storage metrics # @alldata # # 2.4.1 fnmpcrcr - mark parent keys based on parent|child key defined # defined for the record # @alldata -> @alldata # # get the parent based on parent/child key criteria # add kv of the parent entity to child node # {$iekv{kv}}->{pkvs}{parent_key_value}=1 # # 2.4.2 fnmpcros - generate parent child relationship between os visible # entities in different storage_layers # lookup the index %ieosp{os_identifier} to get the os_identifiers # and the key_values # build the look up lists # list of os_identifiers for a key_value @{%iekv{kv}->{ospid}} # list of kvs for a ospid %ieosp{ospid}{ospid}{kv}=1 # build the alias for os paths in list structure @{%ieosp{alias}{$kv}} # if a osid has kvs in different storage layers # refer $config{slh} to identify the parent entity and child entity # if a osid has kvs in the same storage layers # refer $config{seh} to identify the parent and child entity # push the kv of the parent as to # {$iekv{kv}}->{pkvs}{parent_key_value}=1 # # 2.4.3 fncabk - collate and build the key_value, parent_key_value list # for each kv in %iekv # look up the %iekv{kv}->{pkvs} hash structure # keep track of child kv in %iekv{kv}->{cnlist}{ckv}=1 # keep track of parent kv in %iekv{kv}->{pnlist}{pkv}=1 # # 2.4.4 fnbltbnd ( local function ) # - identify the top nodes %tpnds - nodes with no parents no keys # %iekv{kv}->{pnlist} == 0 # - identify the bottom nodes %btmnds- nodes with no children keys # %iekv{kv}->{cnlist} == 0 # # traverse tree depth first post order from %tpnds with do nothin function # fnnpnull to be executed at each node, this will open any closed loops which can # be accessed from top nodes # # traverse tree depth first post order from %btmnds with do nothin function # fnnpnull to be executed at each node, this will open any closed loops # which can be accessed by bottom nodes # # identify the nodes not traversed # traverse tree depth first post order from each non traversed node with do nothing # function fnnpnull to execute at each node, this will open any completely closed # loops not accessed by both top and bottom traversal # # these three traversal will open all closed loops and make sure every node # gets traversed. # # 2.4.5 fnbltbnd ( local function ) # execute this function again to rebuild top and bottom node lists # # traverse tree depth first pre order from %tpnds with fnnpsppcr function # to be executed at each node, this function will remove relationship between # adjacent nodes, by checking fro any additional condition if defined # using $config{sppcr} # # traverse tree depth first pre order from %btmnds with fnnpsppcr function # to be executed at each node, this function will remove relationship between # adjacent nodes, by checking fro any additional condition if defined # using $config{sppcr} # # 2.4.6 fnbltbnd ( local function ) # execute this function again to rebuild top and bottom node lists # # 2.4.7 traverse tree breadth first postorder and remove entities with size 0 # and having no parents - fnnpdelzs # fnnpdelzs - Remove top nodes of size 0 # top nodes of size 0 are not useful for storage computations # nor are they critical for getting the storage layout # this reduces clutter in the reposity and helps performance # eg leave out 0 size partitions of a disk # or leave out a 0 size disk # the traversal should be from top down from top nodes. # remove entities which are in $config{delzs} # remove the entitu from %iekv # remove the relationship between this entity and its children # using fmrmel # # 2.4.8 fnbltbnd ( local function ) # execute this function again to rebuild top and bottom node lists # # traverse tree depth first post order and compute guid for each entity # - function fnnpguid # traverse tree depth first post order and mark container entities # - dnnpmce # traverse tree depth first post order and mark virtual entities # - dnnpmve # traverse tree depth first post order and mark unallocated entities # - dnnpmue # traverse tree depth first post order and generate query flag for # entities - dnnpgqf # traverse tree breadth first preorder and compute size for # entities - dnnpcsz # traverse tree depth first post order and compute free size for # entities - dnnpcfsz # traverse tree depth first post order and compute raw size for # entities - dnnpcrsz # # 2.5 fncfcsi - check for consistency issues in processed data # - mapping errors # - invalid size related errors # # 2.6 fnftlder - added function fnftlder to check for data issues # which may result in loader errors # check if any of the long fields to be encrypted are not null # %storage_data -> # # 2.7 fninmet - instrument the data, alias,keys metrics # from the %iekv, %ieop{alias} and %iekv{kv}->{pkvs} hash structure # %storage_data -> %storage_data # instrument the data metrics to %storage_data{data} add entities in %iekv # # instrument the alias metrics to storage_data{alias} # the alias metric maps os path to osid. add entities from %ieosp{alias}{kv} # # build the list %storage_data{keys} for each parent_key_value in %iekv # , the %iekv{kv}->{pkvs} hash gives the pkv for each kv, # ensure that parent child relation is instrumented only once in the key # using index %icpkh as %icpkh{kv}{pkv}=1 # for nodes with no pkv add an entry to %storage_data{keys} with kv as # pkv # # 2.8 fncsmtf - cache all the metrics (data, keys, alias, issues) to files # on disk # %storage_data -> # # # 3. fndsmff - display the data for the requested metric from the cached # file on disk # 'metric_name'-> # # # check for error messages and log them as em_warnings # # 4. restore_stderr # # b) N'ary tree traversal routines # fntrdf - traverse nary tree depth first pre|post order with closed loop # check and closed loop opening, mark traversed nodes # # c) fnbrclp - open closed loop by breaking the relationship # between the current node and the last node on the stack # \%current_node , $traverse_tag, \%traverse_stack # # get the last node in the stack, the last node will have the # max index value in the stack traversal hash array # break the closed loop by invoking fnrmel # d) fnrmel - break relation between two nodes # \%curnode, \%lastnode , $tvtag -> # clean up of the current node from the traverse lists of # last node in the traverse stack # delete the previous node in the stack from the opposite # traverse list of the current node # remove the node and the last node in the stack from each # others {pkvs} lists # # # c) fntrdfpo - traverse nary tree depth first post order # invoke fntrdf with 'postorder' flag. # d) fntrdfpr - traverse nary tree depth first pre order # invoke fntrdf with 'preorder' flag. # e) fntrbf - traverse tree breadth first in pre or post as suggested by he flag # f) fntrbfpr - traverse nary tree breadth first pre order # g) fntrbfpo - traverse nary tree breadth first post order # # ------------------------------------------------------------------------------- # # MODIFIED (MM/DD/YY) # rajverma 09/15/08 - backport ag config file changes # aptrived 03/21/07 - Backport sejain_bug-5197714 from main # sejain 02/17/07 - bug 5197714 : changing ERROR to WARN for storage # entity # ajdsouza 07/20/05 - clean cached files to prevent stale metrics from being # loaded # ajdsouza 07/07/05 - dump raw metrics before validation # ajdsouza 06/21/05 - remove 0 size partitions # ajdsouza 05/07/05 - add validations for real time regression test # ajdsouza 04/11/05 - bug fix for 3848194 # - changes in fnpcsm # - before any processing traverse the tree in both # directions to open closed loops # - mark traversed nodes, # - identify the nodes not traversed and traverse them # - rebuild top and bottom node traverse lists after # making sure all nods are traversed # - added function fnftlder to check for data issues # which may result in loader errors # - added function fnnmsppcr to remove relationship between # nodes on additional conditions # ajdsouza 03/15/05 - bug fix for 3848194 # no repeat child gids in parent gid # no cyclic parent child relationship between # immediate entities # add function to eliminate closed loops # document sequence of execution # ajdsouza 03/03/05 - fix the unallocated bug for entities with only # virtual parents # ajdsouza 02/16/05 - fix bug related to null gid for disk solstice # entities # ajdsouza 02/02/05 - group query flag functions # ajdsouza 12/16/04 - Use only alloted partitions for disks # ajdsouza 11/16/04 - # ajdsouza 11/11/04 - # ajdsouza 09/28/04 - Add validation of raw data, veritas bug fixes # ajdsouza 09/07/04 - # ajdsouza 08/13/04 - # ajdsouza 08/03/04 - Fixed bug with nfs_server # ajdsouza 07/27/04 - # ajdsouza 07/19/04 - Use the agentstatedir for caching, pretty # indentation # ajdsouza 07/14/04 - Fix closed loop for cached filesystems # ajdsouza 06/29/04 - Bug fix on line 113 # ajdsouza 06/25/04 - storage reporting sources # ajdsouza 06/21/04 - Creation # use strict; use warnings; use File::Spec::Functions; use File::Basename; use File::Path; use Data::Dumper; use storage::Register; # This will ensure locale is set before execution begins # In case of failure in compiling the storage modules, perl will end the script # chances fo script executing in the locale provided by the OS env variables is # reduced use locale; require "emd_common.pl"; $Data::Dumper::Indent = 2; my %config; my %stgcls; # Keep the keys continuous and starting with 1, this count is used to eliminate # the last | and insert the first em_result= $stgcls{data} = { 1=>'key_value', 2=>'global_unique_id', 3=>'name', 4=>'storage_layer', 5=>'em_query_flag', 6=>'entity_type', 7=>'rawsizeb', 8=>'sizeb', 9=>'usedb', 10=>'freeb', 11=>'a1', 12=>'a2', 13=>'a3', 14=>'a4', 15=>'a5', 16=>'a6', 17=>'a7', 18=>'a8' }; $stgcls{keys} = { 1=>'key_value', 2=>'parent_key_value' }; $stgcls{issues} = { 1=>'type', 2=>'message_counter', 3=>'message_nls_id', 4=>'message_params', 5=>'action_nls_id', 6=>'action_params' }; $stgcls{alias} = { 1=>'key_value', 2=>'value' , 3=>'filetype' }; my %mcpf = ( key_value => '%25s', storage_layer => '%18s', entity_type => '%18s', rawsizeb => '%15u', sizeb => '%15u', usedb => '%15u', freeb => '%15u', global_unique_id => '%30s', name => '%20s', parent_key_value => '%25s', os_identifier => '%20s', filetype => '%10s', start => '%7s', end => '%7s' ); $config{slh} = { LOCAL_FILESYSTEM => 4, NFS=> 3, VOLUME_MANAGER => 2, OS_DISK => 1 }; $config{seh}{LOCAL_FILESYSTEM} = { # file and directory are at the same level, they should reside on mountpoint File => 2, Directory => 2, Mountpoint => 1 }; $config{seh}{NFS} = { # file and directory are at the same level, they should reside on mountpoint File => 2, Directory => 2, Mountpoint => 1 }; # special conditions to be met for relationship between entities # for the file or directory in a local_filesystem to be the parents of a # mountpoint the mountpoint should be based on a real filesystem $config{sppcr}{LOCAL_FILESYSTEM}{Mountpoint}{pnlst}{LOCAL_FILESYSTEM}{File}= 'UNIX_FILESYSTEM_RELATIONSHIP_CONDITION'; $config{sppcr}{LOCAL_FILESYSTEM}{Mountpoint}{pnlst}{LOCAL_FILESYSTEM}{Directory}= 'UNIX_FILESYSTEM_RELATIONSHIP_CONDITION'; $config{sppcr}{NFS}{Mountpoint}{pnlst}{NFS}{File}= 'UNIX_FILESYSTEM_RELATIONSHIP_CONDITION'; $config{sppcr}{NFS}{Mountpoint}{pnlst}{NFS}{Directory}= 'UNIX_FILESYSTEM_RELATIONSHIP_CONDITION'; # entities which can be deleted if their size is 0 and # they have no parents $config{delzs}{OS_DISK}{Disk}=1; $config{delzs}{OS_DISK}{'disk partition'}=1; my %scm; # Add a os strign later on $scm{OS_DISK} = { a1 => 'vendor', a2 => 'product', a3 => 'os_identifier', a4 => 'filetype' }; $scm{VOLUME_MANAGER} = { a1 => 'vendor', a2 => 'product', a3 => 'os_identifier', a4 => 'disk_group', a5 => 'configuration', a6 => 'filetype' }; $scm{LOCAL_FILESYSTEM} = { a1 => 'filesystem_type', a2 => 'filesystem', a3 => 'mountpoint' }; $scm{NFS} = { a1 => 'vendor', a2 => 'nfs_server', a3 => 'filesystem', a4 => 'mountpoint', a5 => 'nfs_server_ip_address', a6 => 'nfs_mount_privilege', a7 => 'nfs_server_net_interface_address', a8 => 'nfs_exported_filesystem' }; # Validation my %vcflds; # common layer specific validation my %vsflds; # storage field validation my %veflds; # entity type specific validation # common fields to be validated %vcflds = ( storage_layer=> [( '[\S]+', '^(OS_DISK|VOLUME_MANAGER|LOCAL_FILESYSTEM|NFS)$' )], entity_type=>'[\S]+', name=>'[\S]+', key_value=>'[\S]+', sizeb => '^\d+$' ); # os_disk specific fields to be validated $vsflds{os_disk} = { disk_key=>'[\S]+' }; # disk entity type specific fields to be validated $veflds{os_disk}{disk}= { global_unique_id=>'[\S]+' }; # partition entity type specific fields to be validated $veflds{os_disk}{'disk partition'}= { start=>'[\S]+', end=>'[\S]+' }; # volume manager specific fields to be validated $vsflds{volume_manager} = { vendor=>'[\S]+' }; # subdisk entity type specific fields to be validated $veflds{volume_manager}{'sub disk'}= { start=>'[\S]+' }; $veflds{volume_manager}{'physical entity'}= { start=>'[\S]+' }; # local_filesystem specific fields to be validated $vsflds{local_filesystem} = { filesystem=>'[\S]+' }; $veflds{local_filesystem}{mountpoint}= { mountpoint=>'[\S]+' }; # nfs specific fields to be validated $vsflds{nfs} = { filesystem=>'[\S]+', nfs_server=>'[\S]+' }; $veflds{nfs}{filesystem}= { mountpoint=>'[\S]+', global_unique_id=>'[\S]+' }; # Rules for counting size and raw size my %rfcsz; my %rfcrsz; # rules for adding size to get used for the storage entity lower in the layout $rfcsz{volume_manager}{diskgroup}{volume_manager}{'vm disk'}='INCLUDE'; $rfcsz{volume_manager}{'volume group'}{volume_manager}{'physical volume'}='INCLUDE'; $rfcsz{volume_manager}{'diskset'}{volume_manager}{'device'}='INCLUDE'; # rules for adding raw to get rawsize for the storage entity higher in the layout $rfcrsz{volume_manager}{diskgroup}{volume_manager}{'vm disk'}='INCLUDE'; $rfcrsz{volume_manager}{'volume group'}{volume_manager}{'physical volume'}='INCLUDE'; $rfcrsz{volume_manager}{'diskset'}{volume_manager}{'device'}='INCLUDE'; #List of container entities $config{container}{volume_manager}{'diskgroup'}=1; $config{container}{volume_manager}{'volume group'}=1; $config{container}{volume_manager}{'diskset'}=1; # list of names for spares $config{backup_entities} = 'hot\s*spare|spare|backup|stand\s*by|reserve'; # configuration for identifying virtual devices # virtual devices are present to complete the storage topology, but are # not used in storage calculations # child/parent associations where parent has no explicit boundry on the child $config{is_p_excl_boundry_on_child}{LOCAL_FILESYSTEM}{mountpoint}{LOCAL_FILESYSTEM}{directory}='N'; $config{is_p_excl_boundry_on_child}{NFS}{mountpoint}{LOCAL_FILESYSTEM}{directory}='N'; $config{is_p_excl_boundry_on_child}{NFS}{mountpoint}{NFS}{directory}='N'; $config{is_p_excl_boundry_on_child}{LOCAL_FILESYSTEM}{mountpoint}{LOCAL_FILESYSTEM}{mountpoint}='N'; $config{is_p_excl_boundry_on_child}{NFS}{mountpoint}{LOCAL_FILESYSTEM}{mountpoint}='N'; $config{is_p_excl_boundry_on_child}{NFS}{mountpoint}{NFS}{mountpoint}='N'; # child/parent associations where size of parent is by default factored into the # size/used of the child $config{is_parent_size_in_child}{LOCAL_FILESYSTEM}{mountpoint}{LOCAL_FILESYSTEM}{file}='Y'; $config{is_parent_size_in_child}{NFS}{mountpoint}{LOCAL_FILESYSTEM}{file}='Y'; $config{is_parent_size_in_child}{NFS}{mountpoint}{NFS}{file}='Y'; # entities where unallocated entities can be considered to be virtual $config{virtual_entity}{OS_DISK}{'disk partition'}=1; $config{virtual_entity}{VOLUME_MANAGER}{volume_partition}=1; # entities which can have partial free space $config{can_have_partial_free_space}{OS_DISK}{disk}=1; $config{can_have_partial_free_space}{VOLUME_MANAGER}{'vm disk'}=1; $config{can_have_partial_free_space}{VOLUME_MANAGER}{'physical volume'}=1; $config{can_have_partial_free_space}{VOLUME_MANAGER}{'device'}=1; # child and parent node tags # oppsite tags $config{optg}{cnlst}='pnlst'; $config{optg}{pnlst}='cnlst'; # encrypted long metric columns, they cannot hvae a non null value # will result in em loader errors $config{enclngflds}{data} = [ qw ( key_value global_unique_id ) ]; $config{enclngflds}{keys} = [ qw ( key_value parent_key_value ) ]; $config{enclngflds}{alias} = [ qw ( key_value ) ]; # prefered fields to be in the entity chosen in iekv # to represent a key_value $config{pfdflds}{LOCAL_FILESYSTEM}{entity_type}= { 1 => 'Mountpoint', 2 => 'Directory', 3 => 'File' }; $config{pfdflds}{NFS}{entity_type}= { 1 => 'Mountpoint', 2 => 'Directory', 3 => 'File' }; # Variables for keeping look up indexes my %iekv; # index all storage entities on key vale my %iguid; # index entities on global uid my %ieosp; # index entities with os_identifer my %tpnds; # list of top nodes my %btmnds; # list of bottom nodes my %indnthsh; # has to keep track of indentation while printing the tree #----------------------------------------------------------------------------------------------------------- # Declare subs #----------------------------------------------------------------------------------------------------------- sub fninit ( ); # initialization - prepare logging directories sub fndrmtf ( $\@ ); # dump the raw collected metrics to a file on disk sub fnprnd ( $ ); # print node data sub fnnpprnd ( $$$ ); # print node during traversal sub fnnpgguid ( $$ ); # generate guid for entity during traversal sub fnnpmce( $ ); # mark container entities sub fnnpmue( $ ); # mark unalocated entities sub fnnpmve( $ ); # mark virtual entities sub fnnpmse( $ ); # mark spare entities sub fnnpmbe( $ ); # mark botton entities sub fnnpmte( $ ); # mark top entities sub fnnpgqf( $ ); # generate query flag for entity sub fnnpcsz ( \% ); # populate size for entity sub fnnpcfsz ( $ ); # calculate free size for entity sub fnnpcrsz ( $ ); # calculate raw size for entity sub fnnpnull ( @ ) { return 1 }; # function to do nothing during node traversal sub fntrdf( $$$\&;\% ); # traverse nary tree depth first in order sub fntrdfpo ( $$\&;\% ); # traverse nary tree depth first post order sub fntrdfpr ( $$\&;\% ); # traverse nary tree depth first pre order sub fntrbf ( $$$\& ); # traverse tree breadth first sub fntrbfpo ( $$\& ); # traverse tree breadth first post order sub fntrbfpr ( $$\& ); # traverse tree breadth first pre order sub fnvrawd ( \@ ); # validate raw data sub fnprawd ( \@ ); # prepare the raw data for processing processing sub fnmpcrcr( \@ ); # mark parent key values based on key criteria sub fnmpcros( ); # generate parent child rel for entities with os_identifier sub fnrmrel (\%\%$); # remove the parent/child relationship between two nodes sub fnbrclp( \%$\% ); # check for closed loops sub fnnpsppcr( \%$\% ); # remove relationship beteen entities which do not meet any # additional condiftions defined sub fnnpdelzs ( \%$\%); # remove 0 sized storage entities without parents from the # instrumented list sub fncabk( ); # collate and build parent keys for all entities sub fncrsm( \@ ); # collect the raw storage metrics sub fnblds( \@ ); # build look up data structures for collected metrics sub fnpcsm( \@ ); # process the collected metrics sub fncfcsi ( ); # check for consistency issues sub fninmet( \% ); # instrument the data, alias keys metrics # ( key_value, parent_key_value) sub fnftlder( \% ); # check for fatal data errors which may result in # failure to load data sub fncsmtf( \% ); # cache the processed metrics to file on disk sub fngsm ( ); # generate storage metrics sub fndsmff( $ ); # display processed metrics from file sub fncshrm(); # remove the cached metric files sub restore_stderr(); sub exit_fail(); #---------------------------------------------------------------------------- # SIGNAL handler for warn to Log error and warning messages #---------------------------------------------------------------------------- # die will exit the program with a warn message sub handle_warn { my ( $message ) = @_; my $mtype; chomp $message; $message =~ s/^\s+|\s+$// if $message; return unless $message; # log the message to the log file as WARN only if critical # Else Log it as DEBUG message if(($message =~ /^ERROR\s*:/i) or ($message =~ /root suid enabled/i)) { EMD_PERL_WARN("STORAGE_REPORTS:$message"); } else { EMD_PERL_DEBUG("STORAGE_REPORTS:$message"); } # For the suid enabled we want to log the error return 1 unless $message =~ /root suid enabled/i; # log only errors to the em respository, ignore debug and trace messages ( $mtype, $message ) = ( $message =~ /^(ERROR)\s*:\s*(.+)/i ) if $message =~ /^ERROR\s*:/i; $message =~ s/^\s+|\s+$// if $message; # nmhs has :: between error and message , so remove the extra : $message =~ s/^:+// if $message; storage::Register::log_error_message($message) if $mtype and $mtype =~ /ERROR/i and $message; return 1; } #---------------------------------------------------------------------------- # SIGNAL handler for die to Log error and warning messages #---------------------------------------------------------------------------- # die will exit the program with a warn message sub handle_error { my ( $message ) = @_; my $mtype; chomp $message; $message =~ s/^\s+|\s+$// if $message; return unless $message; # log the message to the log file EMD_PERL_WARN("STORAGE_REPORTS:$message"); # should the message be loaded to the repository return 1 unless $message =~ /^ERROR\s*:/i; # log only errors to the em respository, ignore debug and trace messages ( $mtype, $message ) = ( $message =~ /^(ERROR)\s*:\s*(.+)/i ) if $message =~ /^ERROR\s*:/i; $message =~ s/^\s+|\s+$// if $message; # nmhs has :: between error and message , so remove the extra : $message =~ s/^:+// if $message; storage::Register::log_error_message($message) if $mtype and $mtype =~ /ERROR/i and $message; return 1; } $SIG{'__DIE__'} = sub { handle_error( @_ ); exit_fail() }; $SIG{'__WARN__'} = sub { handle_warn( @_)}; #-------------------------------------------------------------------------------- # FUNCTION : fninit # # DESC # Perform the initialization steps # Prepare the directory for logging # # ARGUMENTS # #-------------------------------------------------------------------------------- sub fninit ( ) { # Directory for creating the nmhs<metric>.txt files # diractory pattern emagent_state/storage/<target_name> $config{smddr} = get_agentstatetarget_dir() or warn "ERROR:Failed to get target name directory to cache metrics for target on host \n" and return; # Save the STDERR before redirecting it to logfile open(OLDERR,">&STDERR"); # If there is an error opening a log file, redirect stderr to null #open(STDERR,"> $devnull"); } #-------------------------------------------------------------------------------- # TRAVERSAL ROUTINES #-------------------------------------------------------------------------------- # dump the raw data to the raw file, useful for debugging sub fndrmtf ( $\@ ) { my ( $file_name,$array_ref ) = @_; my %columns = ( 1=>'key_value', 2=>'storage_layer', 3=>'entity_type', 4=>'rawsizeb', 5=>'sizeb', 6=>'usedb', 7=>'freeb', 8=>'global_unique_id', 9=>'name', 10=>'parent_key_value', 11=>'os_identifier', 12=>'start', 13=>'end' ); my @all_rows; warn "Failed to dump raw data , invalid arguments\n" and return unless ( $file_name and $array_ref ); warn "Failed to dump raw data ,the second arg should be an array \n" and return unless ref($array_ref) =~ /ARRAY/i; for my $metric_ref ( @$array_ref ) { my $row_to_file = ''; for my $order ( sort { $a <=> $b } keys %columns ) { my $column = $columns{$order}; warn "print format is not defined for $column\n" and next unless $mcpf{$column}; $row_to_file = sprintf("%s$mcpf{$column}",$row_to_file,$metric_ref->{$column}) and next if $metric_ref->{$column}; $row_to_file = sprintf("%s$mcpf{$column}",$row_to_file,0) and next if $mcpf{$column} =~ /u/; $row_to_file = sprintf("%s$mcpf{$column}",$row_to_file,'-'); } push @all_rows,$row_to_file; } my $raw_metrics_file = catfile($config{smddr},$file_name); stat($raw_metrics_file); open(FH,'>',$raw_metrics_file) or warn "Failed to open the file $raw_metrics_file to dump raw metrics while generating storage metrics\n" and return; # Print the metric data for my $row ( sort @all_rows ) { print FH "$row\n"; } # print the dat as a dump to file print FH "-------------- Raw metric Dump ------------------------------\n"; print FH Dumper($array_ref); close(FH) or warn "Failed to close the file $raw_metrics_file while generating metrics \n" and return 1; return 1; } sub fnprnd ( $ ) { my ( $node ) = @_; my %columns = ( 1=>'storage_layer', 2=>'entity_type', 3=>'name', 4=>'key_value', 5=>'sizeb', 6=>'usedb', 7=>'freeb', 8=>'em_query_flag' ); my %prefix = ( sizeb=>'(s', usedb=>'u', freeb=>'f'); my %suffix = (); for my $order ( sort { $a <=> $b } keys %columns ) { my $column = $columns{$order}; next unless defined $node->{$column}; print " $prefix{$column}$node->{$column}$suffix{$column}" and next if $prefix{$column} and $suffix{$column}; print " $prefix{$column}$node->{$column}" and next if $prefix{$column}; print " $node->{$column}$suffix{$column}" and next if $suffix{$column}; print " $node->{$column}"; } print " ) "; } # print the node as part of the storage layout tree # with indentation sub fnnpprnd ( $$$ ) { my ($node, $tvtag , $stack_ref) = @_; warn "Storage entity passed for generating global unique id is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, #print it as root of the layout print " +\n" and return 1 if defined $node->{NODE_TYPE}; # How deep is the node my $indent = keys %{$stack_ref}; for ( 1..$indent ) { next unless $_ < $indent; print "| " and next if $indnthsh{$_}; print " "; } if ( $node->{storage_layer} ) { print "|--->"; fnprnd($node); print "\n"; } return 1 if defined $node->{$tvtag} and $node->{$tvtag} and keys %{$node->{$tvtag}}; for ( 1..$indent ) { print "| " and next if $indnthsh{$_}; print " "; } print "\n"; return 1; } # generate global_unique_id for each entity sub fnnpgguid ($$) { my ($node, $tvtag ) = @_; my %cgidix; warn "ERROR:Storage entity passed for generating global unique id is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "ERROR:Reference passed for generating global unique id not an storage entity \n" and return unless $node->{key_value}; warn "ERROR:No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; warn "ERROR:Traverse direction is unknown\n" and return unless $tvtag =~ /cnlst|pnlst/; # the node has already been traversed and # guid generated return 1 if $node->{global_unique_id}; # if the node has children, build the gids by # appending the gids from the children if ( defined $node->{cnlst} and $node->{cnlst} and keys %{$node->{cnlst}} ) { for my $kv ( keys %{$node->{cnlst}} ) { next if defined $iekv{$kv}->{em_query_flag} and $iekv{$kv}->{em_query_flag} =~ /CONTAINER/i; warn "ERROR:Global unique ID is null for $kv while generating gid for $node->{key_value}\n" and return unless $iekv{$kv}->{global_unique_id}; # the index on child gids ensures that child gids are not repeated # to build gid for the parent $cgidix{$iekv{$kv}->{global_unique_id}} = 1 } # sort the child gids alphabetically for my $gid ( sort { $a cmp $b } keys %cgidix ) { # the first time , global_unique_id = global_unique_id of child, subsequent # child nodes are appended with a _ $node->{global_unique_id} = $gid and next unless $node->{global_unique_id}; $node->{global_unique_id} .= "_$gid"; } } if ( not $node->{global_unique_id} ) { my $target_guid = get_target_id() or warn "ERROR:Failed to get the target_id for generating a global unique id for $node->{key_value}\n" and return; $node->{global_unique_id} = "$target_guid\_$node->{key_value}" if $target_guid; } if ( $node->{global_unique_id} ) { $node->{global_unique_id} .= "_S$node->{start}" if $node->{start}; $node->{global_unique_id} .= "_E$node->{end}" if $node->{end}; } return 1 if $node->{global_unique_id}; warn "ERROR:Failed to generate a global unique id for $node->{name} \n" and return if $node->{name}; warn "ERROR:Failed to generate a global unique id for $node->{key_value} \n" and return; } # If top most in a layer then TOP # if bottom in a layer then BOTTOM # if intermediate then INTERMEDIATE # if container then CONTAINER # if not allocaed then UNALLOCATED # Flag spares too # mark container entities sub fnnpmce ($) { my ($node) = @_; warn "Storage entity passed for generating query flag is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "Storage entity reference passed for generating query flag has no key_value\n" and return unless $node->{key_value}; warn "No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; # it is a container node return 1 unless $config{container}{lc $node->{storage_layer}} and $config{container}{lc $node->{storage_layer}}{lc $node->{entity_type}}; # A container node $node->{query_flag}{CONTAINER} = 1; # a container is also a virtual node $node->{query_flag}{VIRTUAL} = 1; delete $node->{query_flag}{UNALLOCATED}; delete $node->{query_flag}{BOTTOM}; delete $node->{query_flag}{TOP}; return 1; } # mark unallocated entities sub fnnpmue ($) { my ($node) = @_; warn "Storage entity passed for generating query flag is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "Storage entity reference passed for generating query flag has no key_value\n" and return unless $node->{key_value}; warn "No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; # if the entity has parent nodes other than containers and virtual # it is allocated if ( defined $node->{pnlst} and $node->{pnlst} and keys %{$node->{pnlst}} ) { # the node is allocated if the parent node is other than a container for my $key ( keys %{$node->{pnlst}} ) { # no query flags so parent not a container, child node is allocated return 1 unless $iekv{$key}->{query_flag} and keys %{$iekv{$key}->{query_flag}}; # the parent is container node , go to the next parent next if $iekv{$key}->{query_flag} and $iekv{$key}->{query_flag}{CONTAINER}; # the parent is virtual node , go to the next parent next if $iekv{$key}->{query_flag} and $iekv{$key}->{query_flag}{VIRTUAL}; # this parent entity is not a container, so the child entity is # allocated return 1; } } # No parent nodes and not a container $node->{query_flag}{UNALLOCATED} = 1; # check for any other query flags which are conditional on the # entity being unallocated return 1 unless ( $node->{possible_query_flag} and $node->{possible_query_flag}{for} and $node->{possible_query_flag}{for}{UNALLOCATED} and ref($node->{possible_query_flag}{for}{UNALLOCATED}) =~ /HASH/i and keys %{$node->{possible_query_flag}{for}{UNALLOCATED}} ); # tick the conditional query flags that depend on # entity being unallocated for my $qflg ( keys %{$node->{possible_query_flag}{for}{UNALLOCATED}} ) { $node->{query_flag}{uc $qflg} = 1; } return 1; } # Mark virtual entities ( Neither TOP, BOTTOM, INTERMEDIATE or CONTAINER ) #( these entities are required to display topology but be left out when #calculating storage numbers ) sub fnnpmve ($) { my ($node) = @_; warn "Storage entity passed for generating query flag is not a reference\n" and return unless ref($node); #top or bottom node, not an os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "Storage entity reference passed for generating query flag has no key_value\n" and return unless $node->{key_value}; warn "No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; # the parent has child nodes if ( defined $node->{cnlst} and $node->{cnlst} and keys %{$node->{cnlst}} ) { for my $key ( keys %{$node->{cnlst}} ) { my $ei = $iekv{$key}; # if a child is virtual the parent is marked virtual if ( $ei->{query_flag} and $ei->{query_flag}{VIRTUAL} ) { $node->{query_flag}{VIRTUAL}=1; return 1; } # if the parent does not have a explicit boundry on the child that can be # identified,- ( Default has explicit boundry ) # eg drectory on a mountpoint does not have explicit boundry if ( $config{is_p_excl_boundry_on_child}{$ei->{storage_layer}} and $config{is_p_excl_boundry_on_child}{$ei->{storage_layer}}{lc $ei->{entity_type}} and $config{is_p_excl_boundry_on_child}{$ei->{storage_layer}}{lc $ei->{entity_type}}{$node->{storage_layer}} and $config{is_p_excl_boundry_on_child}{$ei->{storage_layer}}{lc $ei->{entity_type}}{$node->{storage_layer}}{lc $node->{entity_type}} and $config{is_p_excl_boundry_on_child}{$ei->{storage_layer}}{lc $ei->{entity_type}}{$node->{storage_layer}}{lc $node->{entity_type}} =~ /^N$/i ) { # if a parent entity does not have a explicit boundry on the child # we take it to be virtual , irrespective of whether it reprsents the whole # child or not, that way its out of the storage calculations $node->{query_flag}{VIRTUAL}=1; # Does the parent without explicit boundry represent the whole child #- ( Default represents whole child ) #$node->{query_flag}{VIRTUAL}=1 # if $config{represents_whole_child}{$ei->{storage_layer}} # and $config{represents_whole_child}{$ei->{storage_layer}}{lc $ei->{entity_type}} # and $config{represents_whole_child}{$ei->{storage_layer}}{lc $ei->{entity_type}}{$node->{storage_layer}} # and $config{represents_whole_child}{$ei->{storage_layer}}{lc $ei->{entity_type}}{$node->{storage_layer}}{lc $node->{entity_type}} # and $config{represents_whole_child}{$ei->{storage_layer}}{lc $ei->{entity_type}}{$node->{storage_layer}}{lc $node->{entity_type}} =~ /^N$/i; return 1; } # the parent has an explicit boundry on the child which can be identified # ( Default ) # the parent size is already factored in the size/used space of the child # - ( Default Not Factored ) # eg. file on a mountpoint, its size if already factored in the used /free of the mountpoint if ( $config{is_parent_size_in_child} and $config{is_parent_size_in_child}{$ei->{storage_layer}} and $config{is_parent_size_in_child}{$ei->{storage_layer}}{lc $ei->{entity_type}} and $config{is_parent_size_in_child}{$ei->{storage_layer}}{lc $ei->{entity_type}}{$node->{storage_layer}} and $config{is_parent_size_in_child}{$ei->{storage_layer}}{lc $ei->{entity_type}}{$node->{storage_layer}}{lc $node->{entity_type}} and $config{is_parent_size_in_child}{$ei->{storage_layer}}{lc $ei->{entity_type}}{$node->{storage_layer}}{lc $node->{entity_type}} =~ /^Y$/i ) { $node->{query_flag}{VIRTUAL}=1; return 1; } # the parent has an explicit boundry on the child which can be identified # ( Default ) # the parent size is not factored in the size/used space of the child - # ( Default is Not Factored ) # the unallocated entity of this type can be a virtual entity # mark this as a possibility , the mark_unalloacted function will check # this and mark it # virtual if its found to be unalloacted if ( $config{virtual_entity}{$node->{storage_layer}} and $config{virtual_entity}{$node->{storage_layer}}{lc $node->{entity_type}} ) { $node->{possible_query_flag}{for}{UNALLOCATED}{VIRTUAL}=1; } } } return 1; } # mark spare entities sub fnnpmse ($) { my ($node) = @_; warn "Storage entity passed for generating query flag is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "Storage entity reference passed for generating query flag has no key_value\n" and return unless $node->{key_value}; warn "No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; return 1 unless ( $node->{configuration} and $node->{configuration} =~ /$config{backup_entities}/i ); # Spare node has configuration spare, hotspare etc in it $node->{query_flag}{SPARE} = 1; delete $node->{query_flag}{UNALLOCATED} if $node->{query_flag}{UNALLOCATED}; return 1; } # Mark bottom entities sub fnnpmbe ($) { my ($node) = @_; warn "Storage entity passed for generating query flag is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "Storage entity reference passed for generating query flag has no key_value\n" and return unless $node->{key_value}; warn "No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; # container and virtual nodes are nither top/bottom/intermediate # they are not to be part of summary computation return 1 if ( $node->{query_flag} and ( $node->{query_flag}{VIRTUAL} or $node->{query_flag}{CONTAINER} ) ); # No child nodes and not a container if ( defined $node->{cnlst} and $node->{cnlst} and keys %{$node->{cnlst}} ) { # if child nodes are in another layer set query_flag to BOTTOM for my $key ( keys %{$node->{cnlst}} ) { # skip container entities in another layer next if defined $iekv{$key}->{query_flag} and $iekv{$key}->{query_flag}{CONTAINER}; # skip virtual entities in another layer next if defined $iekv{$key}->{query_flag} and $iekv{$key}->{query_flag}{VIRTUAL}; # if the child node is in a different storage_layer set query_flag to #_BOTTOM_ $node->{query_flag}{BOTTOM} = 1 if $iekv{$key}->{storage_layer} ne $node->{storage_layer}; return 1 if $node->{query_flag}{BOTTOM}; } # all child nodes are in the same layer # if its a the bottom layer will have no child nodes which are other # than containers for my $key ( keys %{$node->{cnlst}} ) { # no query flags, so this child is not a container, and the # parent node is not bottom return 1 unless $iekv{$key}->{query_flag} and keys %{$iekv{$key}->{query_flag}}; # the child is container node , go to the next child node next if $iekv{$key}->{query_flag} and $iekv{$key}->{query_flag}{CONTAINER}; # the child is virtual node , go to the next child node next if $iekv{$key}->{query_flag} and $iekv{$key}->{query_flag}{VIRTUAL}; # this child entity is not a container, so the parent entity is # not bottom return 1; } # no child nodes which are other than containers $node->{query_flag}{BOTTOM} = 1; } else # no child nodes { $node->{query_flag}{BOTTOM} = 1; } return 1; } # Mark top entities sub fnnpmte ($) { my ($node) = @_; warn "Storage entity passed for generating query flag is not a reference\n" and return unless ref($node); #top or bottom node, not an os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "Storage entity reference passed for generating query flag has no key_value\n" and return unless $node->{key_value}; warn "No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; ; # container and virtual nodes are nither top/bottom/intermediate # they are not to be part of summary computation return 1 if ( $node->{query_flag} and ( $node->{query_flag}{VIRTUAL} or $node->{query_flag}{CONTAINER} ) ); # No parent nodes other than container and virtual if ( defined $node->{pnlst} and $node->{pnlst} and keys %{$node->{pnlst}} ) { # if parent nodes are in another layer set query_flag to TOP for my $key ( keys %{$node->{pnlst}} ) { # the node is a container, skip to the nex parent node next if defined $iekv{$key}->{query_flag} and $iekv{$key}->{query_flag}{CONTAINER}; # the parent is virtual node , go to the next parent node next if $iekv{$key}->{query_flag} and $iekv{$key}->{query_flag}{VIRTUAL}; # if the parent node is in a different storage_layer set query_flag to _TOP_ $node->{query_flag}{TOP} = 1 if $iekv{$key}->{storage_layer} ne $node->{storage_layer}; return 1 if $node->{query_flag}{TOP}; } # all parent nodes are in the same layer # if its a the top layer , it will have no parent nodes other than container # and virtual entities for my $key ( keys %{$node->{pnlst}} ) { # this parent node has no query_flags so its nither a container or virtual # node and the child node is not a top node return 1 unless $iekv{$key}->{query_flag} and keys %{$iekv{$key}->{query_flag}}; # the parent is container node , go to the next parent node next if $iekv{$key}->{query_flag} and $iekv{$key}->{query_flag}{CONTAINER}; # the parent is virtual node , go to the next parent node next if $iekv{$key}->{query_flag} and $iekv{$key}->{query_flag}{VIRTUAL}; # this parent entity is nither a container nor virtual, so the parent # entity is not top return 1; } # no parent nodes which are other than containers $node->{query_flag}{TOP} = 1; } else # no parent nodes { $node->{query_flag}{TOP} = 1; } return 1; } # generate the em_query_flag from the query_flag hash sub fnnpgqf( $ ) { my ($node) = @_; warn "Storage entity passed for generating query flag is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "Storage entity reference passed for generating query flag has no key_value\n" and return unless $node->{key_value}; warn "No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; fnnpmse( $node ) or return; fnnpmbe( $node ) or return; fnnpmte( $node ) or return; # for virtual nodes, no top, intermediate flags if ( $node->{query_flag}{VIRTUAL} ) { delete $node->{query_flag}{TOP}; delete $node->{query_flag}{INTERMEDIATE}; } # Intermediate if neither top , bottom or virtual $node->{query_flag}{INTERMEDIATE} = 1 unless ( $node->{query_flag}{BOTTOM} or $node->{query_flag}{TOP} or $node->{query_flag}{VIRTUAL} ); # Create the flag string for my $flag ( sort keys %{$node->{query_flag}} ) { next unless $flag; $node->{em_query_flag} .= "_$flag" unless ( defined $node->{em_query_flag} and $node->{em_query_flag} =~ /$flag/ ); } # add the extra _ at the end $node->{em_query_flag} .= "_" if $node->{em_query_flag} and $node->{em_query_flag} !~ /_$/; return 1; } # populate the sizeb for a node where sizeb is null sub fnnpcsz (\%) { my ($node) = @_; warn "Storage entity passed for populating size is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "ERROR:Reference passed for populating size not an storage entity \n" and return unless $node->{key_value}; warn "ERROR:No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; # Initialize these values before computation for my $field ( qw ( sizeb ) ) { $node->{$field} = 0 unless ( defined $node->{$field} and $node->{$field} =~ /^\d+$/ ); } # If free space is already computed then return return 1 if $node->{sizeb} > 0; # The the current node has child nodes, ie the node below this in # the storage topology if ( defined $node->{cnlst} and $node->{cnlst} and ref ( $node->{cnlst} ) =~ /HASH/i and keys %{$node->{cnlst}} ) { my $child_sizeb = 0; my %hash_global_unique_id; # Go thru each parent node, ie the node on top of current node # in the storage topology for my $kv ( keys %{$node->{cnlst}} ) { my $cndref = $iekv{$kv}; # skip container children next if defined $cndref->{em_query_flag} and $cndref->{em_query_flag} =~ /CONTAINER/; # If Global Unique ID is null then its a processing issue, add the size warn "ERROR:Global unique ID not found for $cndref->{name} \n" and return unless $cndref->{global_unique_id}; # we require a valid size next unless ( defined $cndref->{sizeb} and $cndref->{sizeb} =~ /^\d+$/ ); # if global unique id is he same take the size of the child $node->{sizeb} = $cndref->{sizeb} and return 1 if $node->{global_unique_id} =~ /^$cndref->{global_unique_id}$/ and defined $cndref->{sizeb} and $cndref->{sizeb} =~ /^\d+$/; # This child entity has already been added next if $cndref->{global_unique_id} and $hash_global_unique_id{$cndref->{global_unique_id}}; # if there rules of calculating size # count the child size only if rules indicate to be included if ( $rfcsz{lc $node->{storage_layer}} and $rfcsz{lc $node->{storage_layer}}{lc $node->{entity_type}} and $rfcsz{lc $node->{storage_layer}}{lc $node->{entity_type}} {lc $cndref->{storage_layer}} and $rfcsz{lc $node->{storage_layer}}{lc $node->{entity_type}} {lc $cndref->{storage_layer}}{lc $cndref->{entity_type}} ) { next unless $rfcsz{lc $node->{storage_layer}}{lc $node->{entity_type}} {lc $cndref->{storage_layer}} {lc $cndref->{entity_type}} =~ /INCLUDE/i; } # Keep an running total of the sizeb and usedb for the parent nodes $child_sizeb += $cndref->{sizeb} if $cndref->{sizeb} and $cndref->{sizeb} =~ /^\d+$/; # Keep a record that this global unique if has been added $hash_global_unique_id{$cndref->{global_unique_id}} = 1 if $cndref->{global_unique_id}; } $node->{sizeb} = $child_sizeb if defined $child_sizeb and $child_sizeb =~ /^\d+$/; } return 1; } # Generate the free sizeb for each entity sub fnnpcfsz ($) { my ($node) = @_; warn "Storage entity passed for calculating free size is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "Reference passed for calculating free size not an storage entity \n" and return unless $node->{key_value}; warn "No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; # Initialize these values before computation for my $field ( qw ( sizeb usedb freeb rawsizeb ) ) { $node->{$field} = 0 unless ( defined $node->{$field} and $node->{$field} =~ /^\d+$/ ); } # If free space is already computed then return return 1 if $node->{freeb} > 0; # For a spare entity all its space is marked as used $node->{usedb} = $node->{sizeb} if $node->{em_query_flag} and $node->{em_query_flag} =~ /_SPARE_/i; # if used space is already computed then compute free space and return if ( $node->{usedb} > 0 and $node->{usedb} <= $node->{sizeb} ) { $node->{freeb} = $node->{sizeb}-$node->{usedb}; return 1; } # If the current node is a virtual node all space should be free if ( $node->{em_query_flag} =~ /_VIRTUAL_/i ) { $node->{freeb} = $node->{sizeb}; $node->{usedb}= 0; return 1; } # If the current node is unallocated # set free=size and used = 0 if ( $node->{em_query_flag} =~ /_UNALLOCATED_/i ) { $node->{freeb} = $node->{sizeb}; $node->{usedb}= 0; return 1; } # compute free space for allocated entities # set used=size and freeb = 0 # if the allocated current node cannot have partially free space if ( not $config{can_have_partial_free_space} or not $config{can_have_partial_free_space}{$node->{storage_layer}} or not $config{can_have_partial_free_space}{$node->{storage_layer}}{lc $node->{entity_type}} ) { $node->{usedb} = $node->{sizeb}; $node->{freeb}= 0; return 1; } # The the current node has parent nodes, ie the node on top of this in # the storage topology if ( defined $node->{pnlst} and $node->{pnlst} and ref($node->{pnlst}) =~ /HASH/i and keys %{$node->{pnlst}} ) { my $parent_sizeb = 0; my $parent_usedb = 0; my %hash_global_unique_id; # Go thru each parent node, ie the node on top of current node # in the storage topology for my $kv ( keys %{$node->{pnlst}} ) { my $pndref = $iekv{$kv}; # containers and virtual entities are not part of the storage # calculation they are used fro mapping purposes only next if defined $pndref->{em_query_flag} and $pndref->{em_query_flag} =~ /CONTAINER|VIRTUAL/; # If Global Unique ID is null then its a processing issue, add the size warn "ERROR:Global unique ID not found for $pndref->{name} \n" unless $pndref->{global_unique_id}; # This entity has already been added next if $pndref->{global_unique_id} and $hash_global_unique_id{$pndref->{global_unique_id}}; # if there rules of calculating size # count the parent size only if rules indicate to be included if ( $rfcsz{lc $node->{storage_layer}} and $rfcsz{lc $node->{storage_layer}}{lc $node->{entity_type}} and $rfcsz{lc $node->{storage_layer}}{lc $node->{entity_type}} {lc $pndref->{storage_layer}} and $rfcsz{lc $node->{storage_layer}}{lc $node->{entity_type}} {lc $pndref->{storage_layer}}{lc $pndref->{entity_type}} ) { next unless $rfcsz{lc $node->{storage_layer}}{lc $node->{entity_type}} {lc $pndref->{storage_layer}} {lc $pndref->{entity_type}} =~ /INCLUDE/i; } # Layer specific checks # Keep an running total of the sizeb and usedb for the parent nodes $parent_sizeb += $pndref->{sizeb} if $pndref->{sizeb} and $pndref->{sizeb} =~ /^\d+$/; $parent_usedb += $pndref->{usedb} if $pndref->{usedb} and $pndref->{usedb} =~ /^\d+$/; # Keep a record that this global unique if has been added $hash_global_unique_id{$pndref->{global_unique_id}} = 1 if $pndref->{global_unique_id}; } # if size of parents is > size of entity # set free to 0 and all space as used ( $node->{freeb} = 0 , $node->{usedb} = $node->{sizeb} ) and return 1 if $node->{sizeb} and $parent_sizeb >= $node->{sizeb}; $node->{freeb} = $node->{sizeb}-$parent_sizeb; $node->{usedb} = $node->{sizeb}-$node->{freeb}; } else # the no parents case , ie no storage entities on top of this { #initialize before we start calculating the size of the parents $node->{usedb} = 0, $node->{freeb} = $node->{sizeb}; } return 1; } # Generate the raw sizeb for each entity sub fnnpcrsz ($) { my ($node) = @_; # Flag spares too warn "Storage entity passed for calculating raw size is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "Reference passed for calculating raw size is not an storage entity \n" and return unless $node->{key_value}; warn "No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; # Initialize these values before computation for my $field ( qw ( sizeb usedb freeb rawsizeb ) ) { $node->{$field} = 0 unless ( defined $node->{$field} and $node->{$field} =~ /^\d+$/ ); } # If free space is already computed then return return 1 if $node->{rawsizeb} > 0; # initialize before computation $node->{rawsizeb} = $node->{sizeb}; # if the current nodes has child nodes, nodes which are lower in the storage # layout if ( defined $node->{cnlst} and $node->{cnlst} and keys %{$node->{cnlst}} ) { my $child_rawsizeb = 0; my %hash_global_unique_id; # Go thru each child node, nodes which are lower in the storage layout for my $kv ( keys %{$node->{cnlst}} ) { my $cndref = $iekv{$kv}; next if defined $cndref->{em_query_flag} and $cndref->{em_query_flag} =~ /CONTAINER/; # If Global Unique ID is null log a processing issue, add the rawsize warn "ERROR:Global unique ID not found for $cndref->{name} \n" unless $cndref->{global_unique_id}; # This entity has already been added next if $cndref->{global_unique_id} and $hash_global_unique_id{$cndref->{global_unique_id}}; # if there rules of calculating rawsize # count the child rawsize only if rules indicate to be included if ( $rfcrsz{lc $node->{storage_layer}} and $rfcrsz{lc $node->{storage_layer}}{lc $node->{entity_type}} and $rfcrsz{lc $node->{storage_layer}}{lc $node->{entity_type}} {lc $cndref->{storage_layer}} and $rfcrsz{lc $node->{storage_layer}}{lc $node->{entity_type}} {lc $cndref->{storage_layer}} {lc $cndref->{entity_type}} ) { next unless $rfcrsz{lc $node->{storage_layer}}{lc $node->{entity_type}} {lc $cndref->{storage_layer}} {lc $cndref->{entity_type}} =~ /INCLUDE/i; } # Keep a running total of the rawsizes of the children $child_rawsizeb += $cndref->{rawsizeb} if $cndref->{rawsizeb} and $cndref->{rawsizeb} =~ /^\d+$/; # Keep a record that this global unique if has been added $hash_global_unique_id{$cndref->{global_unique_id}} = 1 if $cndref->{global_unique_id}; } return 1 if $child_rawsizeb and $child_rawsizeb <= $node->{sizeb}; $node->{rawsizeb} = $child_rawsizeb; } return 1; } # remove the parent/child relationship between two nodes sub fnrmrel (\%\%$) { my ( $node, $prvnd, $tvtag ) = @_; # break the closed loop # clean up the current node from the traverse lists of # last node in the traverse stack delete $prvnd->{$tvtag}{$node->{key_value}} if $prvnd->{$tvtag} and $prvnd->{$tvtag}{$node->{key_value}}; # clear the last node in the stack from the current nodes # traverse list in the opposite direction warn "Failed to get the tag name to traverse in opposite direction from $tvtag, while trying to break closed loop\n" and return unless $config{optg}{$tvtag}; delete $node->{$config{optg}{$tvtag}}{$prvnd->{key_value}} if $node->{$config{optg}{$tvtag}} and $node->{$config{optg}{$tvtag}}{$prvnd->{key_value}}; # remove the node and the last node in the stack from # each others {pkvs} lists delete $node->{pkvs}{$prvnd->{key_value}} if $node->{pkvs} and $node->{pkvs}{$prvnd->{key_value}}; delete $prvnd->{pkvs}{$node->{key_value}} if $prvnd->{pkvs} and $prvnd->{pkvs}{$node->{key_value}}; return 1; } # open closed loop by breaking the relationship between the current node # and the last node on the stack sub fnbrclp(\%$\%) { my ( $node, $tvtag, $stack_ref ) = @_; # get the last node in the stack, # the last node will have the max index value my $depth = keys %{$stack_ref}; warn "Failed to get the depth of the stack , while trying to break a closed loop\n" and return unless $depth; warn "Failed to get $depth storage entity on the stack , while trying to break a closed loop\n" and return unless $stack_ref->{$depth}; # get the last node on the stack my $lnd = $stack_ref->{$depth}; # remove the relationship between the node # and previous node fnrmrel(%$node,%$lnd,$tvtag) or return; return 1; } # if there are additional conditions for parent child relationship # remove the relationships between the node and its # related nodes in the traverse direction which do not meet this # condition sub fnnpsppcr(\%$\%) { my ( $node, $tvtag, $stack_ref ) = @_; warn "Storage entity passed for determining parent child relationship is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "Reference passed for determining parent child relationship is not an storage entity \n" and return unless $node->{key_value}; warn "No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; # return if there is no special condition defined for this entity type return 1 unless ( $config{sppcr}{$node->{storage_layer}} and $config{sppcr}{$node->{storage_layer}}{$node->{entity_type}} and $config{sppcr}{$node->{storage_layer}}{$node->{entity_type}}{$tvtag} ); # the node should have entity related to it in the # traverse direction return 1 unless $node->{$tvtag} and ref($node->{$tvtag}) =~ /HASH/i and keys %{$node->{$tvtag}}; # get the list of key values fro the entity in the traverse # direction my @kvs = keys %{$node->{$tvtag}}; # for each of the key values check for special condition for my $kv ( @kvs ) { my $prvnd = $iekv{$kv}; # nothing to do if a special condition is not defined for relationships with # this entity and the node in the tvtag direction return 1 unless ( $config{sppcr}{$node->{storage_layer}}{$node->{entity_type}}{$tvtag}{$prvnd->{storage_layer}} and $config{sppcr}{$node->{storage_layer}}{$node->{entity_type}}{$tvtag}{$prvnd->{storage_layer}}{$prvnd->{entity_type}} ); # the filesystem check on unix # files and directories should only map to the mountpoint which has a real filesystem if ( $config{sppcr}{$node->{storage_layer}}{$node->{entity_type}}{$tvtag} {$prvnd->{storage_layer}}{$prvnd->{entity_type}} =~ /UNIX_FILESYSTEM_RELATIONSHIP_CONDITION/i ) { # the mountpoint node should have children (filesystem) next unless $node->{cnlst} and ref($node->{cnlst}) =~ /HASH/i and keys %{$node->{cnlst}}; # get the list of child key values for the mountpoint my @ckvs = keys %{$node->{cnlst}}; # at least one of the child keys should be a filesystem entity # file or directory should map to the mountpoint based on a filesystem my @rslt= map { 'MOUNTPOINT_BASED_ON_FILESYSTEM' if ( $iekv{$_} and $iekv{$_}->{entity_type} =~ /Filesystem/i ) } @ckvs; next if grep /MOUNTPOINT_BASED_ON_FILESYSTEM/,@rslt; # before we remove the relationship between this file/directory and mountpoint # the file or directory entity should have atleast one other child relationships next unless $prvnd->{cnlst} and ref($prvnd->{cnlst}) =~ /HASH/i and keys %{$prvnd->{cnlst}} > 1; # remove the relationship between entities fnrmrel(%$prvnd,%$node,$tvtag) or return; } } return 1; } # Remove top nodes of size 0 # top nodes of size 0 are not useful for storage computations # nor are they critical for getting the storage layout # this reduces clutter in the reposity and helps performance # eg leave out 0 size partitions of a disk # or leave out a 0 size disk sub fnnpdelzs(\%$\%) { my ( $node, $tvtag, $stack_ref ) = @_; warn "Storage entity passed for determining parent child relationship is not a reference\n" and return unless ref($node); #top or bottom node, not a os entity node, do not process return 1 if defined $node->{NODE_TYPE}; warn "Reference passed for determining parent child relationship is not an storage entity \n" and return unless $node->{key_value}; warn "No storage layer defined for $node->{key_value}\n" and return unless $node->{storage_layer}; # add a warning error if tvtag is not cnlst warn "ERROR: The breadth first traverse direction for deletign 0 sized top entities should be childnodes instead of $tvtag \n" and return unless $tvtag =~ /cnlst/; # If there is a parent node then this node is not a # a candidate for deletion return 1 if ( defined $node->{pnlst} and $node->{pnlst} and keys %{$node->{pnlst}} ); # The node has no parents # the node size > 0 # then its not a candidate for deletion return 1 if $node->{sizeb}; # If the storage layer and entity type are to be deleted # if they are size 0 and have no parents then return. # use the config variable to determine this. return 1 unless ( $config{delzs}{$node->{storage_layer}} and $config{delzs}{$node->{storage_layer}}{lc $node->{entity_type}} ); #------------------------------------------------------------- # The node is a top node with 0 size and should be deleted #------------------------------------------------------------- #print "deleting node ".Dumper($node); # if the node has child entities # remove the relationship before deleting the node if ( $node->{$tvtag} and ref($node->{$tvtag}) =~ /HASH/i and keys %{$node->{$tvtag}} ) { # get the list of key values fro the entity in the traverse # direction ( cnlst ) my @kvs = keys %{$node->{$tvtag}}; # for each of the key values check for special condition for my $kv ( @kvs ) { my $prvnd = $iekv{$kv}; # remove the relationship between parent child entities entities fnrmrel(%$prvnd,%$node,$tvtag) or return; } } #print "removing node $node->{name} $node->{sizeb}\n"; # Delete the node from the list and look up # data structures delete $iekv{$node->{key_value}}; return 1; } # Check for closed loops in the tree my %clpstk; # Traverse the tree based on the start and tag passed # and the specified order sub fntrdf( $$$\&;\% ) { my ($order,$node, $tvtag, $fnptr,$stack_ref) = @_; # This is a closed loop, the stack keeps track of the node # pointers already traversed if ( $clpstk{$tvtag}{$node} ) { warn "Node $node->{storage_layer} $node->{entity_type} $node->{key_value} is in a closed loop when traversing $tvtag\n "; fnbrclp(%$node,$tvtag,%$stack_ref) or return; return 1; } # mark the node as traversed if previously not marked so # this will help identify nodes which are in completely closed # loops $node->{traversed}=1 unless $node->{traversed}; # the depth of the tree at any point # is the size of the stack my $depth = keys %{$stack_ref}; $depth +=1; $stack_ref->{$depth}=$node; # keep an index of the node pointer , for closed loop check $clpstk{$tvtag}{$node}=1; # preorder processing if ( $order =~ /preorder/i) { if ( $fnptr ) { # Execute the function to execute before traversing down the tree if ( not $fnptr->($node, $tvtag, \%$stack_ref ) ) { delete $stack_ref->{$depth}; delete $clpstk{$tvtag}{$node}; return; } } } # traverse each child node specified by the traverse_tag if ( defined $node->{$tvtag} and $node->{$tvtag} and ref($node->{$tvtag}) =~ /HASH/i and keys %{$node->{$tvtag}} ) { # List of keys from this node to traverse in the direction # specified by traverse tag my @kvs = sort keys %{$node->{$tvtag}}; # count of nodes to travserse from this node my $node_count = @kvs; for my $i ( 1..$node_count ) { my $kv = $kvs[$i-1]; next unless $kv; warn "Failed to find the storage entity $kv in the indexed list\n" and return unless $iekv{$kv}; my $next_node = $iekv{$kv}; next unless $next_node; bless $next_node; # keeps track of the pending children at any # depth in the layout $indnthsh{$depth+1} = $node_count-$i; if ( not fntrdf ( $order, $next_node, $tvtag, &$fnptr, %$stack_ref ) ) { # unwind the stack and the closed loop index delete $stack_ref->{$depth}; delete $clpstk{$tvtag}{$node}; return; } } } # postorder processing if ( $order =~ /postorder/i) { if ( $fnptr ) { # Execute the function to execute before traversing down the tree if ( not $fnptr->($node, $tvtag, \%$stack_ref ) ) { delete $stack_ref->{$depth}; delete $clpstk{$tvtag}{$node}; return; } } } # unwind the stack and the closed loop index delete $stack_ref->{$depth}; delete $clpstk{$tvtag}{$node}; return 1; } #Traverse the tree post order sub fntrdfpo( $$\&;\% ) { my ($node, $tvtag, $fnptr,$stack_ref) = @_; return fntrdf('postorder',$node, $tvtag, &$fnptr,%$stack_ref); } #Traverse the tree pre order sub fntrdfpr( $$\&;\% ) { my ($node, $tvtag, $fnptr,$stack_ref) = @_; return fntrdf('preorder',$node, $tvtag, &$fnptr,%$stack_ref); } # Traverse tree breadth first sub fntrbf( $$$\& ) { my ( $order, $node, $tvtag, $fnptr) = @_; my %trvstak; my @queue; # initialize the queue push @queue, $node; # while there are nodes in the queue while ( ( my $nnode = shift @queue) ) { # If the node has been processed skip it next if $trvstak{$tvtag}{$nnode}; # keep track of the processed nodes $trvstak{$tvtag}{$nnode}=1; # preorder processing - process node before pushing the children to the queue if ( $order =~ /preorder/i) { if ( $fnptr ) { # invoke the function on the nnode if ( not $fnptr->($nnode,$tvtag) ) { warn "Failed executing the function in fntrbf for node \n"; return; } } } # if the nnode has children push them to the queue if ( $nnode # and ref($nnode) =~ /HASH/i and $nnode->{$tvtag} and ref($nnode->{$tvtag}) =~ /HASH/i and keys %{$nnode->{$tvtag}} ) { # push each child node to the queue for my $ckv ( keys %{$nnode->{$tvtag}} ) { next unless $ckv; warn "Failed to find the $tvtag key_value $ckv in iekv for node \n" and return unless $iekv{$ckv}; my $cnd = $iekv{$ckv}; warn "The $tvtag node key_value $ckv is not a hash in iekv for node \n" and return unless ( $cnd # and ref($cnd) =~ /HASH/i and keys %{$cnd} ); push @queue,$cnd; } } # postorder processing - process node after pushing the children to the queue if ( $order =~ /postorder/i) { if ( $fnptr ) { # invoke the function on the nnode if ( not $fnptr->($nnode,$tvtag) ) { warn "Failed executing the function in fntrbf for node \n"; return; } } } } return 1; } #Traverse the tree breadth first post order sub fntrbfpo( $$\& ) { my ($node, $tvtag, $fnptr) = @_; return fntrbf( 'postorder',$node, $tvtag, &$fnptr ); } #Traverse the tree beadth first pre order sub fntrbfpr( $$\& ) { my ($node, $tvtag, $fnptr,$stack_ref) = @_; return fntrbf( 'preorder',$node, $tvtag, &$fnptr ); } #-------------------------------------------------------------------------------- # Validate the collected raw metrics from the perl modules sub fnvrawd ( \@ ) { my ( $mlref ) = @_; # Perform validation of the fields between metic record and validation list sub fnvmfld ( \%\% ) { my ( $tref,$fref) = @_; # Make sure the arguments passed are refs to hashes warn "Failed in fnvmfld, hash ref expected for first argument \n" unless ( $tref and ref($tref) =~ /HASH/i ); warn "Failed in fnvmfld, hash ref expected for second argument \n" unless ( $fref and ref($fref) =~ /HASH/i ); # validate each field in the validate has list for my $field ( keys %{$fref} ) { # The field should be present warn "ERROR:Metric column $field not instrumented\n" and return unless defined $tref->{$field}; # data type check not required next unless $fref->{$field}; # build a list of the conditions # # the conditions may be sclar or a list of # conditions which are in a list array my @fcrts; # if the conditions are scalar push @fcrts,$fref->{$field} unless ref($fref->{$field}); # if the conditions are a list @fcrts = @{$fref->{$field}} if ref($fref->{$field}) and ref($fref->{$field}) =~ /ARRAY/i; next unless @fcrts; # perform data type check for my $fcrtr ( @fcrts ) { warn "ERROR:Metric column $field is not of $fcrtr type\n" and return unless $tref->{$field} =~ /$fcrtr/; } } return 1 } warn "Failed in validate_raw_data, array ref expected for argument\n" and return unless ref($mlref) =~ /ARRAY/i; for my $mhref ( @{$mlref} ) { warn "Failed in validate_raw_data, expected hash ref of metrics in each array element\n" and return unless ref($mhref) =~ /HASH/i; # Remove any spaces at the begining and end for each value for my $fkey ( keys %{$mhref} ) { next unless $mhref->{$fkey}; $mhref->{$fkey} =~ s/^\s+|\s+$//g; } # perform common validations fnvmfld(%{$mhref},%vcflds) or return; # perform storage layer specific validations ( fnvmfld ( %{$mhref}, %{$vsflds{lc $mhref->{storage_layer}}} ) or return ) if $mhref->{storage_layer} and $vsflds{lc $mhref->{storage_layer}} and ref($vsflds{lc $mhref->{storage_layer}}) =~ /HASH/i; # perform entity type specific validations ( fnvmfld ( %{$mhref}, %{$veflds{lc $mhref->{storage_layer}}{lc $mhref->{entity_type}}} ) or return ) if $mhref->{storage_layer} and $mhref->{entity_type} and $veflds{lc $mhref->{storage_layer}}{lc $mhref->{entity_type}} and ref($veflds{lc $mhref->{storage_layer}}{lc $mhref->{entity_type}}) =~ /HASH/i; # validate for sizeb usedb freeb values # used or free should be less than size for my $fchk ( qw ( usedb freeb ) ) { warn "ERROR: $fchk is greater than size for enity $mhref->{entity_type} $mhref->{name},size=$mhref->{sizeb}, $fchk=$mhref->{$fchk}\n" and return if $mhref->{$fchk} and $mhref->{$fchk} > $mhref->{sizeb} } # used + free should be <= size warn "ERROR: used+free is greater than size for enity $mhref->{entity_type} $mhref->{name}, size=$mhref->{sizeb} usedb=$mhref->{usedb} freeb=$mhref->{freeb}\n" and return if $mhref->{usedb} and $mhref->{freeb} and ($mhref->{usedb}+$mhref->{freeb}) > $mhref->{sizeb}; } return 1; } # prepare the raw dtaa for further processing # strip leading/trailing blanks # append stoage layer to key_value, parent_key_value # append storage_layer to parent/child key criteria sub fnprawd ( \@ ) { my ( $stgeref ) = @_; # process the collected metrics # appent key_value and parent_key_value with storage_layer # remove trailing and leading spaces from all entities for my $mhdref ( @{$stgeref} ) { # remove trailing and leading spaces from all entity data for my $hash_key ( keys %$mhdref ) { next unless $mhdref->{$hash_key}; $mhdref->{$hash_key} =~ s/^\s+|\s+$//g; $mhdref->{$hash_key} =~ s/^-+$//g; # replace blank spaces in entity type with - $mhdref->{$hash_key} =~ s/-/ /g if $hash_key =~ /entity_type/i; } # ERROR If there is NO key_value warn "key_value cannot be null for an instrumented mertic\n" and return unless $mhdref->{key_value}; # The key value is specific to a layer, so append it with the storage_layer # wherever it is refered $mhdref->{key_value} = "$mhdref->{storage_layer}_$mhdref->{key_value}"; # the instrumented parent_key_value always refers to # entities within the storage layer # prepend storage_layer to parent_key_value $mhdref->{parent_key_value} = "$mhdref->{storage_layer}_$mhdref->{parent_key_value}" if $mhdref->{parent_key_value}; # append the key_values defined in the parent and chld_key_criteria with # the storage_layer for my $hshk ( qw ( parent_entity_criteria child_entity_criteria ) ) { next unless $mhdref->{$hshk}; next unless ref($mhdref->{$hshk}) =~ /ARRAY/i; # update key_value in each criteria for my $crt ( @{$mhdref->{$hshk}} ) { next unless ref($crt) =~ /HASH/i; # append the storage layer to criteria if its not # in there yet $crt->{storage_layer} = $mhdref->{storage_layer} unless $crt->{storage_layer}; # update criteria if it has a key_value next unless $crt->{key_value}; # append storage_layer from criteria $crt->{key_value} = "$crt->{storage_layer}_$crt->{key_value}" and next if $crt->{storage_layer}; # append storage_layer from the metrics hash $crt->{key_value} = "$mhdref->{storage_layer}_$crt->{key_value}" if $mhdref->{storage_layer}; } } } return 1; } # clean up cached nmhs files sub fncshrm() { # remove each cached file if it exists for my $mnm( qw ( data keys issues alias rmet fcsh ) ) { # Dump the metrics to the file my $flnm = catfile($config{smddr},'nmhs'.substr($mnm,0,4).'.txt'); stat($flnm); unlink($flnm) or warn "ERROR:Failed to remove the cached metric file $flnm, the metrics loaded may be stale\n" if -e $flnm; } return 1; } # Collect all the data sub fncrsm( \@ ) { my ( $amdr ) = @_; warn "Failed to collect raw data ,the first arg should be an array \n" and return unless ref($amdr) =~ /ARRAY/i; # Get List of functions to execute in the top down order, store this order as # the hierarchy for storage layers here # Loop thru them and execute them and store the results in the array # Process the array my $amhrff; my %lsmf = ( 3 => \&storage::Register::get_disk_metrics , 2 => \&storage::Register::get_virtualization_layer_metrics, 1 => \&storage::Register::get_filesystem_metrics ); my %tsmf = ( 3 => 'storage::Register::get_disk_metrics' , 2 => 'storage::Register::get_virtualization_layer_metrics', 1 => 'storage::Register::get_filesystem_metrics' ); for my $fnordr ( sort {$a cmp $b} keys %lsmf ) { # execute raw function to collect metrics for each layer # How to handle stdout $amhrff = $lsmf{$fnordr}->() or warn "Failed to execute function $tsmf{$fnordr}\n" and return; # print Dumper($amhrff); push @{$amdr}, @$amhrff if $amhrff and @$amhrff; } # dump raw metrics to file fndrmtf('nmhsrmet.log',@{$amdr}) or warn "Failed to dump the raw data \n" and return; # Validate the collected raw data fnvrawd(@$amdr) or warn "Failed to validate the raw data \n" and return; fnprawd( @$amdr ) or warn "Failed to prepare raw data for analysis \n" and return; return 1; } # index <key_value><os_identifier>=ref # index <os_identifier><key_value>=ref # index <key_value>=ref # index <key_value>->{pkvs}{parent_key_value}=1 # Build the quick look up hash indexes , datastructures with references sub fnblds( \@ ) { my ( $alldata_ref ) = @_; warn "Failed to build the lookup data structure ,the first arg should be an array \n" and return unless ref($alldata_ref) =~ /ARRAY/i; # warn "Failed to build the lookup data structure ,the second argument should be a hash \n" # and return # unless ref($sdref) =~ /HASH/i; # choose the entity to be indexed for each key_value # NOTE: multiple entities can have the same key_value # when storage metrics are instrumented the key_value has to be unique # so multiple distinct entities with different key_values have to be folded # the index %iekv keeps track of the entity for each key_value for my $eref ( @{$alldata_ref} ) { # index <key_value>=ref $iekv{$eref->{key_value}} = $eref unless $iekv{$eref->{key_value}}; # if two distinct entities with different entity types have the same key_value # choose the entity type in the index using the preffered entity type configuration if ( # different entities and prefered fields configured for this storage_layer $eref != $iekv{$eref->{key_value}} and $config{pfdflds}{$eref->{storage_layer}} ) { # select a field to choose between entities my $pfdfld; # get the list of fields with preference for my $pfldch ( keys %{$config{pfdflds}{$eref->{storage_layer}}} ) { # prefered value makes sense only if the entities have distinct # values for this field # else we retain the default in %iekv next if $eref->{$pfldch} and $iekv{$eref->{key_value}}->{$pfldch} and $eref->{$pfldch} =~ /^$iekv{$eref->{key_value}}->{$pfldch}$/; # this field exists in both entities and has different values in each of them # utilize this field to choose between entities $pfdfld = $pfldch and last; } # no meaningful prefered field selected, # retain the current entity refered in %iekv next unless $pfdfld; # get the ordered list of data values for the chosed preferef field # ordered (asc) list of values for the prefered fields for my $pdfidx ( sort { $a <=> $b } keys %{$config{pfdflds}{$eref->{storage_layer}}{$pfdfld}} ) { # get the value based on the index my $pfdval = $config{pfdflds}{$eref->{storage_layer}}{$pfdfld}->{$pdfidx}; next unless $pfdval; # if the existing field value matches the value in the prefered field then # leave it as it is last if $iekv{$eref->{key_value}}->{$pfdfld} =~ /^$pfdval$/i; # if the prefered value for this field matched the value in eref # %iekv will refer to ierf $iekv{$eref->{key_value}} = $eref and last if $eref->{$pfdfld} =~ /^$pfdval$/i; } } } # havign built the %iekv index to look up the entity on key_value # build the additional required look up lists and indexes for my $eref ( @{$alldata_ref} ) { # keep an array of all parent_key_values on the iekv index $iekv{$eref->{key_value}}->{pkvs}{$eref->{parent_key_value}}=1 if $eref->{parent_key_value}; # keep an array of all parent/child key criterias on the iekv index push @{$iekv{$eref->{key_value}}->{parent_key_criteria}}, @{$eref->{parent_key_criteria}} if $eref->{parent_key_criteria} and $eref != $iekv{$eref->{key_value}}; push @{$iekv{$eref->{key_value}}->{child_key_criteria}}, @{$eref->{child_key_criteria}} if $eref->{child_key_criteria} and $eref != $iekv{$eref->{key_value}}; # keep an index of entities with os identifier # # index <key_value><os_identifier>=ref # index <os_identifier><key_value>=ref # # What about entities that cannot be recognized on the OS but can be # shared across Layers, like what ??? - Disks on a windows NT box !! # $ieosp{key_value}{$eref->{key_value}}{$eref->{os_identifier}} = $iekv{$eref->{key_value}}, $ieosp{os_identifier}{$eref->{os_identifier}}{$eref->{key_value}} = $iekv{$eref->{key_value}} if $eref->{os_identifier}; # build the storage_data{data} array to instrument data metrics # push @{$sdref->{data}}, $eref # if $iekv{$eref->{key_value}} == $eref; } return 1; } #-------------------------------------------------------------------------------- # FUNCTION : fnmpcrcr # # DESC # Mark the parent_key_values for entities based on any parent or child key # criteria defined Returns a reference to an array of pointers to disk metric # data as required by EM # # ARGUMENTS # array of the pointer to the metric hashes generated fro OEM 9I # #-------------------------------------------------------------------------------- # the key_values here should have storage layer factored in them # Either move this later on after key_value, parent_key_value and key_value in # criteris are qualified with STORAGE_LAYER sub fnmpcrcr ( \@ ) { my ( $stgeref ) = @_; # Mark the parent_key_values for entities based on any parent or child key # criteria defined for my $kv ( keys %iekv ) { my $eref = $iekv{$kv}; # build the child to parent hash for the entity if the parent_key criteria # is defined if ( $eref->{parent_entity_criteria} and @{$eref->{parent_entity_criteria}} ) { for my $pecr ( @{$eref->{parent_entity_criteria}} ) { next unless keys %{$pecr}; for my $mdref ( @{$stgeref} ) { # an entity cannot be its own child next if $mdref == $eref; # Add a check for key_value too, parent and child cannot have same # key_value next if $eref->{key_value} and $mdref->{key_value} and $eref->{key_value} eq $mdref->{key_value}; # If the key_value of this mdref entity is already a parent # skip to next mdref next if $eref->{pkvs} and $eref->{pkvs}{$mdref->{key_value}}; my @values = map { 'FAILED_PARENT_CHILD_ENTITY_CRITERIA' unless $mdref->{$_} and $mdref->{$_} eq $pecr->{$_} } keys %{$pecr}; next if grep /FAILED_PARENT_CHILD_ENTITY_CRITERIA/,@values; $eref->{pkvs}{$mdref->{key_value}}=1; } } } # build the parent to child hash for the entity if the child_key criteria is # defined if ( $eref->{child_entity_criteria} and @{$eref->{child_entity_criteria}} ) { for my $cecr ( @{$eref->{child_entity_criteria}} ) { next unless keys %{$cecr}; for my $mdref ( @{$stgeref} ) { # an entity cannot be its own child next if $mdref == $eref; # Add a check for key_value too, parent and child cannot have same # key_value next if $eref->{key_value} and $mdref->{key_value} and $eref->{key_value} eq $mdref->{key_value}; # If the key_value of this mdref entity is already a parent # skip to next mdref next if $mdref->{pkvs} and $mdref->{pkvs}{$eref->{key_value}}; my @values = map { 'FAILED_PARENT_CHILD_ENTITY_CRITERIA' unless $mdref->{$_} and $mdref->{$_} eq $cecr->{$_} } keys %{$cecr}; next if grep /FAILED_PARENT_CHILD_ENTITY_CRITERIA/,@values; $mdref->{pkvs}{$eref->{key_value}}=1; } } } } return 1; } # index <os_path>=identifier # index @<identifier>,ref# storage @data,ref # storage @{parent_key}<child_key_value>,<parent_key_value> # storage @<key_value>,<parent_key_value> # Interlink between the entities across different storage layers that are # represented on the OS sub fnmpcros( ) { # my ( $sdref ) = @_; # Loop theu the entities which have as os identifier and build a list of # entities to an ospid # Read from index <os_identifier><key_value>=ref for my $osid ( keys %{$ieosp{os_identifier}} ) { # Be more linient on this return os_path if either of these functions fail my $ospth = storage::Register::get_os_storage_entity_path($osid) or warn "WARN:Failed to get the OS storage entity name for $osid\n" and next; my $ospid = storage::Register::get_os_identifier_for_os_path ($ospth) or warn "WARN:Failed to get the os identifier for storage entity name $ospth\n" and return; my $ftype = storage::Register::get_file_type ($osid) or warn "WARN:Failed to get the filetype for $osid \n" and return; for my $kv ( keys %{$ieosp{os_identifier}{$osid}} ) { my $ie = $iekv{$kv}; $ie->{filetype} = $ftype if $ie->{os_identifier} and $ie->{os_identifier} eq $osid; push @{$ie->{ospid}},$ospid; # index {ospid}{ospid}{kv}=1 $ieosp{ospid}{$ospid}{$ie->{key_value}}=1; # Build the alias list for each os path for a key_value push @{$ieosp{alias}{$kv}},{key_value=>$kv,value=>$osid,filetype=>$ftype} if $kv and $osid; } } # loop thru the entities which have an os_identifier and find entities in # another storage layaer # which have the same entitiy identifier # Read from <key_value><os_identifier>-ref for my $kv ( keys %{$ieosp{key_value}} ) { my $ie = $iekv{$kv}; # There should be an identifier for this os path warn "WARN:Failed to find an identifier on the OS for $ie->{os_identifier}\n" and next unless $ie->{ospid} and @{$ie->{ospid}}; for my $ospid ( @{$ie->{ospid}} ) { # Get all entities with the same identifier as this one # build the list # storage_data @{parent_key}<child_key_value>,<parent_key_value> # for my $okv ( keys %{$ieosp{ospid}{$ospid}} ) { my $iewosid = $iekv{$okv}; # The other entity with the same identifier should be in a different # storage layer than the $kv entity # next unless $iewosid->{storage_layer} ne $ie->{storage_layer}; # Lets map within the os visible entities in a storage layer next unless $iewosid->{key_value} ne $kv; warn "No hierarchy defined for layers\n" and next unless $config{slh}{$ie->{storage_layer}} and $config{slh}{$iewosid->{storage_layer}}; # The parent_key for the entity in the lower storage layer should # have a list of key_values of the entity higher storage layers # Disk < Volume < Filesystem < File if ( $config{slh}{$ie->{storage_layer}} < $config{slh}{$iewosid->{storage_layer}} ) { $ie->{pkvs}{$iewosid->{key_value}}=1; next; } if ( $config{slh}{$ie->{storage_layer}} > $config{slh}{$iewosid->{storage_layer}} ) { $iekv{$iewosid->{key_value}}->{pkvs}{$ie->{key_value}}=1; next; } # If the parent and child are in the same layer then # Do not map within a layer if hierarchy is not defined for both the # layers next unless $config{seh}{$ie->{storage_layer}}{$ie->{entity_type}} and $config{seh}{$iewosid->{storage_layer}}{$ie->{entity_type}}; # check for cyclic dependencies between a immediate parent and child # entities skip if a parent child relationship already exists # between the entities next if ( $ie->{parent_key_value} and $ie->{parent_key_value} eq $iewosid->{key_value} ) or ( $iewosid->{parent_key_value} and $iewosid->{parent_key_value} eq $kv ); # If parent key for the lower entity should have the list of key values # of the higher entity # the lower entity is the child, the higher entity is ther parent # the pkvs of the child have the kv of the parent if ( $config{seh}{$ie->{storage_layer}}{$ie->{entity_type}} < $config{seh}{$iewosid->{storage_layer}}{$iewosid->{entity_type}} ) { $ie->{pkvs}{$iewosid->{key_value}}=1; next; } if ( $config{seh}{$ie->{storage_layer}}{$ie->{entity_type}} > $config{seh}{$iewosid->{storage_layer}}{$iewosid->{entity_type}} ) { $iekv{$iewosid->{key_value}}->{pkvs}{$ie->{key_value}}=1; next; } } } } return 1; } # collate and build parent keys for all entities sub fncabk( ) { # Loop thru each child key_value in # storage @{parent_key}<child_key_value>,<parent_key_value> for my $ckv ( keys %iekv ) { # ERROR If there is NO entry in key master for node warn "key_value cannot be null in key map\n" and return unless $ckv; # Loop thru List of parents from # storage @{pkvs}<child_key_value>,<parent_key_value> for my $pkv ( keys %{$iekv{$ckv}->{pkvs}} ) { # ERROR If there is a parent node and if there is NO entry in key master # for parent node warn "Unable to find the entry in key master for parent node $pkv\n" and return unless $pkv and defined $iekv{$pkv}; # Keep the parent to child relationship between the parent node and child # node for top down traversal $iekv{$pkv}->{cnlst}{$ckv} = 1; # Keep the child to parent relationship between the parent node and child # node for bottom up traversal $iekv{$ckv}->{pnlst}{$pkv} = 1; } } return 1; } # function to build top and bottom node lists # top nodes have no parent nodes # bottom bodes have no child nodes sub fnbltbnd() { # initialize the top and bottom node lists %tpnds = (); %btmnds = (); # Identifier for top and bottom nodes so we can skip processing them # in np_ functions $tpnds{NODE_TYPE}='TOP_NODE'; $btmnds{NODE_TYPE}='BOTTOM_NODE'; # From the node List get the nodes which are the bottom ones, ones with no # children for my $node ( values %iekv ) { # If there is no parent node then this is a top node $tpnds{cnlst}{$node->{key_value}} = 1 unless ( defined $node->{pnlst} and $node->{pnlst} and keys %{$node->{pnlst}} ); # If there are no children then this is a bottom node $btmnds{pnlst}{$node->{key_value}} = 1 unless ( defined $node->{cnlst} and $node->{cnlst} and keys %{$node->{cnlst}} ); } return 1; } # Process colled storage metrics # verify the storage entity tree has no closed loops # open closed loops # make sure all nodes are traversable # traverse tree to do the regular processing sub fnpcsm( \@ ) { my ( $stgeref ) = @_; # mark parent child keys based on parent_key_criteria from raw metrics fnmpcrcr(@{$stgeref}) or warn "ERROR:Failed to mark the parent_key_values based on criteria\n" and return; # mark parent child entities based on is identifier fnmpcros() or return; # collate and build the parent_key_value metric for entities fncabk() or return; # build the top and bottom node list fnbltbnd() or warn "ERROR:Failed to mark top and bottom nodes for traversal - \n" and return; # traverse the tree breaking closed loops # start with the top nodes and traverse child nodes fntrdfpo(\%tpnds ,'cnlst',&fnnpnull) or warn "ERROR:Failed to traverse the storage tree top down to open closed loops\n" and return; # traverse the tree breaking closed loops # start with bottom nodes and traverse the parent nodes fntrdfpo(\%btmnds ,'pnlst',&fnnpnull) or warn "ERROR:Failed to traverse the storage tree bottom up to open closed loops\n" and return; # Check for any non traversed nodes # if nodes are not traversed in both top and bottom traversal # they are in completely closed loops for my $ntkv ( keys %iekv ) { # skip nodes which are traversed next if $iekv{$ntkv} and $iekv{$ntkv}->{traversed}; # begin traversing from the non traversed node # since this is a completely closed loop, any direction # of traversal will open the loop fntrdfpo($iekv{$ntkv} ,'cnlst',&fnnpnull) or warn "ERROR:Failed to traverse the storage tree to break completely closed loops at key $ntkv\n" and return; } # after closed loops are broken # rebuild the top and bottom node list fnbltbnd() or warn "ERROR:Failed to mark top and bottom nodes for traversal - \n" and return; # traverse the tree to remove relationshp between entities which # do not meet the special conditions defined in $config{sppcr} # this is a preorder traversal , we want to remove the relationship before # traversing down a entity fntrdfpr(\%tpnds ,'cnlst',&fnnpsppcr) or warn "ERROR:Failed to remove relation between entities based on additional predicates in top down traversal \n" and return; fntrdfpr(\%btmnds ,'pnlst',&fnnpsppcr) or warn "ERROR:Failed to remove relation between entities based on additional predicates in bottom up traversal \n" and return; # after closed loops are broken # rebuild the top and bottom node list fnbltbnd() or warn "ERROR:Failed to mark top and bottom nodes for traversal - \n" and return; # Remove top nodes of size 0 # top nodes of size 0 are not useful for storage computations # nor are they critical for getting the storage layout # this reduces clutter in the reposity and helps performance # eg leave out 0 size partitions of a disk # or leave out a 0 size disk fntrbfpo(\%tpnds ,'cnlst',&fnnpdelzs) or warn "ERROR:Failed during deleting 0 sized top level storage entities - \n" and return; # after the 0 size top entities are deleted # rebuild the top and bottom node list fnbltbnd() or warn "ERROR:Failed to mark top and bottom nodes for traversal - \n" and return; # gid requires gids for all the children first # so depth first post order from top fntrdfpo(\%tpnds ,'cnlst',&fnnpgguid) or warn "ERROR:Failed to generate the global unique_id for all nodes in the storage tree\n" and return; # no dependency any traverse will do fntrdfpo(\%tpnds ,'cnlst',&fnnpmce) or warn "ERROR:Failed to mark the container entities in the storage tree\n" and return; # since a node is virtual is its child is virtual, so depth first post order # from top fntrdfpo(\%tpnds ,'cnlst',&fnnpmve) or warn "ERROR:Failed to mark the virtual entities in the storage tree\n" and return; # since a node is unalloacted if it has no parents # ( except containers and virtual ) # so depth first post order from bottom fntrdfpo(\%btmnds ,'pnlst',&fnnpmue) or warn "ERROR:Failed to mark the unallocated entities in the storage tree\n" and return; # this will also mark, top, botton and spares in the same traverse # no preference of traverse fntrdfpo(\%tpnds ,'cnlst',&fnnpgqf) or warn "ERROR:Failed to mark the top entities in the storage tree\n" and return; # traverse the tree breadth first pre order fntrbfpr(\%btmnds ,'pnlst',&fnnpcsz) or warn "ERROR:Failed to populate the size for all nodes in the storage tree\n" and return; fntrdfpo(\%btmnds ,'pnlst',&fnnpcfsz) or warn "ERROR:Failed to calculate the free size for all nodes in the storage tree\n" and return; fntrdfpo(\%tpnds ,'cnlst',&fnnpcrsz) or warn "ERROR:Failed to calculate the raw size for all nodes in the storage tree\n" and return; # Print the tree top down if the env is defined if ( $ENV{EM_STORAGE_PRINT_TOPOLOGY} ) { fntrdfpr(\%tpnds ,'cnlst',&fnnpprnd) or die "Failed to print the tree "; # Print the tree bottom up fntrdfpr(\%btmnds ,'pnlst',&fnnpprnd) or die "Failed to mark parent tree indent"; } return 1; } # check for consistency issues in the procssed metrics sub fncfcsi() { # Check for mapping errors # The bottom nodes of higher layers should be parents of lower layer entities for my $kv ( keys %{$btmnds{pnlst}} ) { next unless $iekv{$kv}->{em_query_flag} =~ /_BOTTOM/i; # If the bottom level entity has child entities then there is no issue next if $iekv{$kv}->{cnlst} and keys %{$iekv{$kv}->{cnlst}}; # OS disks and NFS filesystems will not have children, all others should next if $iekv{$kv}->{storage_layer} =~ /^NFS$/i and $iekv{$kv}->{entity_type} =~ /^filesystem$/i; next if $iekv{$kv}->{storage_layer} =~ /^OS_DISK$/i and $iekv{$kv}->{entity_type} =~ /^disk$/i; log_message( 'ERROR_INST_MAPPING','ACTION_INST_RESOLV_ISSUE', $iekv{$kv} ) or warn "Failed to log issue mapping error\n" and return; } # Check for invalid size values for my $kv ( keys %iekv ) { next if $iekv{$kv}->{em_query_flag} =~ /_CONTAINER/i; next if $iekv{$kv}->{sizeb} >= 0 and $iekv{$kv}->{sizeb} >= $iekv{$kv}->{usedb}; log_message( 'ERROR_INST_INVALID_SIZE','ACTION_INST_RESOLV_ISSUE', $iekv{$kv} ) or warn "Failed to log issue for invalid size\n" and return; } return 1; } # instrument the metrics # data from %iekv # alias from %eosp # keys from %iekv{kv}->{pkv} #( key_value, parent_key_value) # from the %iekv{kv}->{pkvs} hash structure # sub fninmet( \% ) { my ( $sdref ) = @_; my %icpkh; # Loop thru each child key_value in # storage @{parent_key}<child_key_value>,<parent_key_value> for my $ckv ( keys %iekv ) { # ERROR If there is NO entry in key master for node warn "key_value cannot be null in key map\n" and return unless $ckv; # build the storage_data{data} array to instrument data metrics push @{$sdref->{data}}, $iekv{$ckv}; # Build the alias metric for each os path for a key_value if ( $ieosp{alias}{$ckv} ) { push @{$sdref->{alias}}, @{$ieosp{alias}{$ckv}}; } # Loop thru List of parents from # storage @{pkvs}<child_key_value>,<parent_key_value> for my $pkv ( keys %{$iekv{$ckv}->{pkvs}} ) { # ERROR If there is a parent node and if there is NO entry in key master # for parent node warn "Unable to find the entry in key master for parent node $pkv\n" and return unless $pkv and defined $iekv{$pkv}; # Build the keys array for reporting # # index {parent_to_child_map}<key_value><parent_key_value> # storage @keys,ref {child_key_value, parent_key_value} # Do not relist the child_key , parent_key again, this is a unique key in # the mgmt_storage_report_keys table next if $icpkh{$ckv} and $icpkh{$ckv}{$pkv}; $icpkh{$ckv}{$pkv} = 1; # The list of keys to be instrumented push @{$sdref->{keys}}, {key_value=>$ckv, parent_key_value=>$pkv}; } # Make sure each key_value has an entry in the list of keys, if it doesnt then # it has no parent or its a top enity in the tree # For a top entity set the parent_key_value to be the same as key_value. # key_value and parent_key_value form the primary key push @{$sdref->{keys}}, {key_value=>$ckv, parent_key_value=>$ckv} unless $icpkh{$ckv} and keys %{$icpkh{$ckv}}; } return 1; } # check for fatal data errors which may result in # failure to load data sub fnftlder(\%) { my ( $sdref ) = @_; # the metrics to be checked for fatal errors for my $mnm ( keys %{$config{enclngflds}} ) { # get each row of instrumented data for this metric for my $ref ( @{$sdref->{$mnm}} ) { # get the list of long fields which are to be encrypted for this metric for my $encfld ( @{$config{enclngflds}{$mnm}} ) { # Check for not null values in long fields next if $ref->{$encfld}; # the long field is null, this will give loader errors # log em_error and skip loading data warn "ERROR:Instrumented Field $encfld is NULL in storage metric $mnm for entity with key $ref->{key_value}, instrumented storage metrics failed to load\n" and return; } } } return 1; } # write the instrumented storage metrics to file sub fncsmtf(\%) { my ( $sdref ) = @_; # Get the issues metric list $sdref->{issues} = storage::Register::get_messages(); # Process each metric for writign to the file for my $mnm( qw ( data keys issues alias ) ) { my @all_rows; # Read all the metric data by row into an array for my $row ( @{$sdref->{$mnm}} ) { my $row_to_file = ''; for my $clordr ( sort {$a <=> $b} keys %{$stgcls{$mnm}} ) { $row_to_file = "$row_to_file$row->{$scm{$row->{storage_layer}}{$stgcls{$mnm}{$clordr}}}|" and next if $row->{storage_layer} and $scm{$row->{storage_layer}}{$stgcls{$mnm}{$clordr}} and $row->{$scm{$row->{storage_layer}}{$stgcls{$mnm}{$clordr}}}; $row_to_file ="$row_to_file$row->{$stgcls{$mnm}{$clordr}}|" and next if $row->{$stgcls{$mnm}{$clordr}}; $row_to_file = "$row_to_file|" unless $clordr == keys % {$stgcls{$mnm}};; } push @all_rows,$row_to_file; } # Dump the metrics to the file my $flnm = catfile($config{smddr},'nmhs'.substr($mnm,0,4).'.txt'); stat($flnm); open(FH,'>',$flnm) or close(FH) and warn "ERROR:Failed to open the mapfile $flnm while generating metrics\n" and return; # Print the column header print FH "columns="; for my $clordr ( sort {$a <=> $b} keys %{$stgcls{$mnm}} ) { print FH "$stgcls{$mnm}{$clordr}|"; } print FH "\n"; # Print the metric data for my $row ( sort @all_rows ) { print FH "$row\n"; } close(FH) or warn "Failed to close the file $flnm while generating metrics \n" and return; } return 1; } # generate the storage metrics and cache it to file sub fngsm() { my %storage_data; my @alldata; # remove the cached files fncshrm() or return; # collect raw metrics fncrsm(@alldata) or return; warn "ERROR:Failed to collected data for analysis and instrumenting storage metrics\n" and return unless @alldata; # build lookup indexes fnblds(@alldata) or return; # mark parent child relationships fnpcsm(@alldata) or return; # check for consistency issues fncfcsi() or return; # instrument the data,alias,keys metric from the processed data fninmet(%storage_data) or return; # check for any data problems which may result in loader errors fnftlder(%storage_data) or return; # cache the processed metrcs to file fncsmtf(%storage_data) or return; return 1; } # display the cached metrics from file sub fndsmff($) { my ( $mnm) = @_; my $flnm = catfile($config{smddr},'nmhs'.substr($mnm,0,4).'.txt'); stat($flnm); # Open the file for reading open(FH,"$flnm") or warn "ERROR:Failed to open the cached file $flnm for $mnm while reading metrics\n" and return; my @cols; # Read each line and prepare it for printing while ( <FH> ) { my %row; chomp; s/^\s+|\s+$//g; # read the title row @cols = split /\|/,substr $_ , length('columns=') and next if $_ =~ /^columns=/; warn "ERROR:Unable to read the field names from file $flnm for metric $mnm\n" and return unless @cols; # get all columns from the data row my @values = split /\|/; @row{ @cols } = @values; for my $clordr ( sort {$a <=> $b} keys % {$stgcls{$mnm}} ) { print "em_result=" if $clordr == 1; if ( $row{$stgcls{$mnm}{$clordr}} ) { print "$row{$stgcls{$mnm}{$clordr}}"; } elsif ( $scm{$mnm}{$stgcls{$mnm}{$clordr}} and $mcpf{$scm{$mnm}{$stgcls{$mnm}{$clordr}}} and $mcpf{$scm{$mnm}{$stgcls{$mnm}{$clordr}}} =~ /u/ ) { print "0"; } else { print "0" if $mcpf{$stgcls{$mnm}{$clordr}} and $mcpf{$stgcls{$mnm}{$clordr}} =~ /u/; } print "|" unless $clordr == keys % {$stgcls{$mnm}}; } print "\n"; } close(FH) or warn "Failed to close the file $flnm while reading metrics\n"; return 1; } #---------------------------------------------------------------------------- # Restore STDERR #---------------------------------------------------------------------------- sub restore_stderr() { # Close the error log file close(STDERR); # Restore back the stderr fd open(STDERR,">&OLDERR"); close(OLDERR); return 1; } sub exit_fail() { restore_stderr(); my $errmsgrf = storage::Register::get_error_messages(); exit 1 unless ( $errmsgrf and ref($errmsgrf) =~ /ARRAY/i and @{$errmsgrf} ); for my $error_message ( @{$errmsgrf} ) { chomp $error_message; $error_message =~ s/^\s+|\s+$//g; next unless $error_message; print "em_error=$error_message\n"; } exit 1; } #--------------------------------------------------- # Begin processing here #--------------------------------------------------- #initialization - prepare logging directories fninit(); #Check the platform #collect data only for the platforms supported. #Supported Platforms : Linux, Solaris, HPUX, AIX, Default is Linux. #my $host_os = hostOSD::getOSType(); my $host_os = `uname -s`; chomp($host_os); my @os_list =("Linux"); @os_list = storage::Utilities::getConfig("OS") if storage::Utilities::getConfig("OS"); my $supported = 0; foreach my $os (@os_list) { last if (uc($os) eq uc($host_os)) and $supported = 1; } if ($supported != 1) { warn "ERROR:The Operating System is not supported by the Host Storage Module." and exit 0 ; } # Read the metric to be instrumented, the default is data for # storage_report_data metric $storage::Register::metric_name = $ARGV[0] if $ARGV[0]; # remove leading or trailing blanks $storage::Register::metric_name =~ s/\s//g if $storage::Register::metric_name; # default storage_reportic metric is data for storage_report_data $storage::Register::metric_name = 'data' unless $storage::Register::metric_name; warn "ERROR:Unsupported storage_report metric $storage::Register::metric_name\n" and exit_fail() unless $storage::Register::metric_name =~ /^(data|keys|issues|alias)$/; # The storage_report_data metric will always cache fresh data to the files. # The keys and issues metrics will read the cached data if it exists , # if no cached files exist they will generate all metrics and cache the metrics my $flnm = catfile($config{smddr},'nmhs'.substr($storage::Register::metric_name,0,4).'.txt'); stat($flnm); ( fngsm or warn "ERROR:Failed to generate storage metrics for $storage::Register::metric_name\n" and exit_fail() ) if ( $storage::Register::metric_name =~ /^data$/ or not -e $flnm ); fndsmff($storage::Register::metric_name) or warn "Failed to read storage metrics for $storage::Register::metric_name from file\n" and exit_fail(); # print the warning and error messages as warnigns to stdout # this will be logged in table mgmt_current_metric_errors by the em agent my $errmsgrf = storage::Register::get_error_messages(); if ( $errmsgrf and ref($errmsgrf) =~ /ARRAY/i and @{$errmsgrf} ) { for my $errmsg ( @{$errmsgrf} ) { chomp $errmsg; $errmsg =~ s/^\s+|\s+$//g; next unless $errmsg; print "em_warning=$errmsg\n"; } } restore_stderr(); exit 0; END { close(STDOUT); }
Ms-Dos/Windows
Unix
Write backup
jsp File Browser version 1.2 by
www.vonloesch.de