/usr/share/arc/InfoChecker.pm is in nordugrid-arc-arex 5.3.0~rc1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | package InfoChecker;
use base 'Exporter';
use strict;
# Class to check that a data structure conforms to a schema. Data and schema
# are both nested perl structures consisting of hashes and arrays nested to any
# depth. This function will check that data and schema have the same nesting
# structure. For hashes, all required keys in the schema must also be defined
# in the data. A "*" value in the schema marks that key optional. A "*" key in
# the schema matches all unmatched keys in the data (if any). Arrays in the
# schema should have exactly one element, and this element will be matched
# against all elements in the corresponding array in the data.
# Constructor
#
# Arguments:
# $schema - reference to the schema structure
sub new($$) {
my ($this,$schema) = @_;
my $class = ref($this) || $this;
die "Schema not a reference" unless ref($schema);
my $self = {schema => $schema};
bless $self, $class;
return $self;
}
#
# Arguments:
# $data - reference to a data structure that should be checked
# $strict - (optional) if true, extra hash keys in data will be reported.
# Otherwise only missing keys are reported.
#
# Returns:
# @errors - list of error messages, one for each mismatch found during
# checking
sub verify($$;$) {
my ($self,$data,$strict) = @_;
$self->{strict} = $strict;
$self->{errors} = [];
$self->_verify_part("",$data,$self->{schema});
return @{$self->{errors}};
}
sub _verify_part($$$$); # prototype it, because it's a recursive function
sub _verify_part($$$$) {
my ($self,$subject,$data,$schema) = @_;
unless (defined $data) {
push @{$self->{errors}}, "$subject is undefined";
return 1; # tell caller this entry can be deleted
}
unless ( ref($data) eq ref($schema) ) {
my $type = ref($schema) ? ref($schema) : "SCALAR";
push @{$self->{errors}}, "$subject has wrong type, $type expected";
return 1; # tell caller this entry can be deleted
}
# process a hash reference
if ( ref($schema) eq "HASH" ) {
# deal with hash keys other than '*'
my @templkeys = grep { $_ ne "*" } keys %$schema;
for my $key ( sort @templkeys ) {
my $subj = $subject."{$key}";
if ( defined $data->{$key} ) {
# check that existing key value is valid
my $can_delete = $self->_verify_part($subj, $data->{$key},
$schema->{$key});
# delete it if it's not valid
if ($can_delete and $self->{strict}) {
push @{$self->{errors}}, "$subj deleting it";
delete $data->{$key};
}
} elsif ($schema->{$key} eq "*") {
# do nothing:
# this missing key is optional
} elsif (ref($schema->{$key}) eq "ARRAY"
and $schema->{$key}[0] eq "*") {
# do nothing:
# this missing key is optional, it points to optional array
} elsif (ref($schema->{$key}) eq "HASH"
and keys(%{$schema->{$key}}) == 1
and exists $schema->{$key}{'*'} ) {
# do nothing:
# this missing key is optional, it points to optional hash
} else {
push @{$self->{errors}}, "$subj is missing";
}
}
# deal with '*' hash key in schema
if ( grep { $_ eq "*" } keys %$schema ) {
for my $datakey ( sort keys %$data ) {
# skip keys that have been checked already
next if grep { $datakey eq $_ } @templkeys;
my $subj = $subject."{$datakey}";
# check that the key's value is valid
my $can_delete = $self->_verify_part($subj, $data->{$datakey},
$schema->{"*"});
# delete it if it's not valid
if ($can_delete and $self->{strict}) {
push @{$self->{errors}}, "$subj deleting it";
delete $data->{$datakey};
}
}
# no '*' key in schema, reverse checking may be performed
} elsif ($self->{strict}) {
for my $datakey ( sort keys %$data) {
my $subj = $subject."{$datakey}";
unless (exists $schema->{$datakey}) {
push @{$self->{errors}}, "$subj is not recognized";
push @{$self->{errors}}, "$subj deleting it";
delete $data->{$datakey};
}
}
}
# process an array reference
} elsif ( ref($schema) eq "ARRAY" ) {
for ( my $i=0; $i < @$data; $i++ ) {
my $subj = $subject."[$i]";
# check that data element is valid
my $can_delete = $self->_verify_part($subj, $data->[$i],
$schema->[0]);
# delete it if it's not valid
if ($can_delete and $self->{strict}) {
push @{$self->{errors}}, "$subj deleting it";
splice @$data, $i, 1;
--$i;
}
}
# process a scalar: nothing to do here
} elsif ( not ref($data)) {
# nothing else than scalars and HASH and ARRAY references are allowed in
# the schema
} else {
my $type = ref($schema);
push @{$self->{errors}},
"$subject bad value in schema, ref($type) not allowed";
}
return 0;
}
#### TEST ##### TEST ##### TEST ##### TEST ##### TEST ##### TEST ##### TEST ####
sub test() {
my $schema = {
totalcpus => '',
freecpus => '',
jobs => {
'*' => { owner => '' }
},
users => [
{ dn => '' }
]
};
my $data = {
freecpus => undef,
jobs => {
id1 => { owner => 'val' },
id2 => 'something else'
},
users => [{dn => 'joe', extra => 'dummy'}, 'bad user', { }]
};
require Data::Dumper; import Data::Dumper;
print "Before: ",Dumper($data);
print "Checker: options->$_\n" foreach InfoChecker->new($schema)->verify($data,1);
print "After: ",Dumper($data);
}
#test;
1;
|