line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Schedule::Load::Hosts::Host.pm -- Loading information about a host |
2
|
|
|
|
|
|
|
# See copyright, etc in below POD section. |
3
|
|
|
|
|
|
|
###################################################################### |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Schedule::Load::Hosts::Host; |
6
|
|
|
|
|
|
|
require 5.004; |
7
|
|
|
|
|
|
|
require Exporter; |
8
|
|
|
|
|
|
|
require AutoLoader; |
9
|
|
|
|
|
|
|
@ISA = qw(Exporter AutoLoader); |
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
6
|
use Schedule::Load qw(_min _max); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
52
|
|
12
|
1
|
|
|
1
|
|
557
|
use Schedule::Load::Hosts::Proc; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
40
|
|
13
|
1
|
|
|
1
|
|
407
|
use Schedule::Load::Safe; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
36
|
|
14
|
|
|
|
|
|
|
|
15
|
1
|
|
|
1
|
|
6
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
64
|
|
16
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
36
|
|
17
|
1
|
|
|
1
|
|
6
|
use vars qw($VERSION $AUTOLOAD $Debug $Safer); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1952
|
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
###################################################################### |
20
|
|
|
|
|
|
|
#### Configuration Section |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Other configurable settings. |
23
|
|
|
|
|
|
|
$VERSION = '3.064'; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
###################################################################### |
26
|
|
|
|
|
|
|
#### Globals |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
$Debug = $Schedule::Load::Debug; |
29
|
|
|
|
|
|
|
$Safer = Schedule::Load::Safe->new(); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
###################################################################### |
32
|
|
|
|
|
|
|
#### Special status |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
sub fields { |
35
|
2
|
50
|
33
|
2
|
1
|
3
|
my $self = shift; ($self && ref($self)) or croak 'usage: '.__PACKAGE__.'->hosts)'; |
|
2
|
|
|
|
|
15
|
|
36
|
2
|
|
|
|
|
3
|
my @keys = keys %{$self->{const}}; |
|
2
|
|
|
|
|
25
|
|
37
|
2
|
|
|
|
|
6
|
push @keys, keys %{$self->{stored}}; |
|
2
|
|
|
|
|
8
|
|
38
|
2
|
|
|
|
|
4
|
push @keys, keys %{$self->{dynamic}}; |
|
2
|
|
|
|
|
13
|
|
39
|
2
|
|
|
|
|
6
|
return (grep {$_ ne "procs"} @keys); |
|
64
|
|
|
|
|
131
|
|
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
sub exists { |
43
|
2
|
50
|
33
|
2
|
1
|
3
|
my $self = shift; ($self && ref($self)) or croak 'usage: '.__PACKAGE__.'->get(field))'; |
|
2
|
|
|
|
|
20
|
|
44
|
2
|
|
|
|
|
3
|
my $field = shift; |
45
|
2
|
|
33
|
|
|
64
|
return (exists ($self->{dynamic}{$field}) |
46
|
|
|
|
|
|
|
|| exists ($self->{stored}{$field}) |
47
|
|
|
|
|
|
|
|| exists ($self->{const}{$field})); |
48
|
|
|
|
|
|
|
} |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
sub get { |
51
|
2
|
50
|
33
|
2
|
1
|
4
|
my $self = shift; ($self && ref($self)) or croak 'usage: '.__PACKAGE__.'->get(field))'; |
|
2
|
|
|
|
|
52
|
|
52
|
2
|
|
|
|
|
3
|
my $field = shift; |
53
|
|
|
|
|
|
|
# Always look at dynamic info first, there might be a override of a const |
54
|
2
|
50
|
|
|
|
21
|
if (exists ($self->{dynamic}{$field})) { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
55
|
0
|
|
|
|
|
0
|
return $self->{dynamic}{$field}; |
56
|
|
|
|
|
|
|
} elsif (exists ($self->{stored}{$field})) { |
57
|
0
|
|
|
|
|
0
|
return $self->{stored}{$field}; |
58
|
|
|
|
|
|
|
} elsif (exists ($self->{const}{$field})) { |
59
|
2
|
|
|
|
|
15
|
return $self->{const}{$field}; |
60
|
|
|
|
|
|
|
} else { |
61
|
0
|
|
|
|
|
0
|
croak __PACKAGE__.'->get($field): Unknown field'; |
62
|
|
|
|
|
|
|
} |
63
|
|
|
|
|
|
|
} |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
sub get_undef { |
66
|
24
|
50
|
33
|
24
|
0
|
34
|
my $self = shift; ($self && ref($self)) or croak 'usage: '.__PACKAGE__.'->get(field))'; |
|
24
|
|
|
|
|
121
|
|
67
|
24
|
|
|
|
|
56
|
my $field = shift; |
68
|
|
|
|
|
|
|
# Always look at dynamic info first, there might be a override of a const |
69
|
24
|
50
|
|
|
|
118
|
if (exists ($self->{dynamic}{$field})) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
70
|
0
|
|
|
|
|
0
|
return $self->{dynamic}{$field}; |
71
|
|
|
|
|
|
|
} elsif (exists ($self->{stored}{$field})) { |
72
|
0
|
|
|
|
|
0
|
return $self->{stored}{$field}; |
73
|
|
|
|
|
|
|
} elsif (exists ($self->{const}{$field})) { |
74
|
12
|
|
|
|
|
201
|
return $self->{const}{$field}; |
75
|
|
|
|
|
|
|
} else { |
76
|
12
|
|
|
|
|
57
|
return undef; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
###################################################################### |
81
|
|
|
|
|
|
|
#### Matching |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub host_match { |
84
|
14
|
50
|
33
|
14
|
0
|
23
|
my $self = shift; ($self && ref($self)) or croak 'usage: '.__PACKAGE__.'->host_match(classesref))'; |
|
14
|
|
|
|
|
77
|
|
85
|
|
|
|
|
|
|
# Params can be either a hash reference (for chooser) |
86
|
|
|
|
|
|
|
# or a list of parameters (simple user functions) |
87
|
14
|
|
|
|
|
32
|
my $paramref = $_[0]; |
88
|
14
|
50
|
|
|
|
34
|
if (!ref $paramref) { |
89
|
14
|
|
|
|
|
41
|
$paramref = {#classes=>[], |
90
|
|
|
|
|
|
|
#match_cb=> undef, |
91
|
|
|
|
|
|
|
#allow_reserved=>1, |
92
|
|
|
|
|
|
|
@_, |
93
|
|
|
|
|
|
|
}; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
# For use of Hosts::hosts_match |
96
|
14
|
|
33
|
|
|
227
|
return ((!defined $paramref->{classes} || $self->classes_match($paramref->{classes})) |
97
|
|
|
|
|
|
|
&& (!defined $paramref->{match_cb} || $self->eval_match ($paramref->{match_cb})) |
98
|
|
|
|
|
|
|
&& (!defined $paramref->{allow_reserved} || $paramref->{allow_reserved} |
99
|
|
|
|
|
|
|
|| !$self->reserved) |
100
|
|
|
|
|
|
|
); |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub host_match_chooser { |
104
|
0
|
|
|
0
|
0
|
0
|
my $self = $_[0]; |
105
|
|
|
|
|
|
|
# Similar to host_match, but for internal use by the chooser - performance critical |
106
|
0
|
|
|
|
|
0
|
my $paramref = $_[1]; |
107
|
0
|
|
|
|
|
0
|
my $scratchref = $_[2]; |
108
|
|
|
|
|
|
|
# For use of Hosts::hosts_match |
109
|
0
|
|
0
|
|
|
0
|
return (( !defined $paramref->{classes} || !defined $paramref->{classes}[0] |
110
|
|
|
|
|
|
|
|| _classes_match_chooser($self, $paramref->{classes}) |
111
|
|
|
|
|
|
|
) |
112
|
|
|
|
|
|
|
&& (!defined $paramref->{match_cb} |
113
|
|
|
|
|
|
|
#Slow, so inlined: || $self->eval_match ($paramref->{match_cb}, $scratchref) |
114
|
|
|
|
|
|
|
|| _eval_generic_cb($self, $paramref->{match_cb}, $scratchref) |
115
|
|
|
|
|
|
|
) |
116
|
|
|
|
|
|
|
&& (!defined $paramref->{allow_reserved} || $paramref->{allow_reserved} |
117
|
|
|
|
|
|
|
|| !$self->reserved) |
118
|
|
|
|
|
|
|
); |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub classes_match { |
122
|
6
|
50
|
33
|
6
|
1
|
13
|
my $self = shift; ($self && ref($self)) or croak 'usage: '.__PACKAGE__.'->classes_match(classesref))'; |
|
6
|
|
|
|
|
44
|
|
123
|
6
|
|
|
|
|
10
|
my $classesref = shift; |
124
|
6
|
50
|
33
|
|
|
40
|
return 1 if !defined $classesref || !defined $classesref->[0]; # Null reference means match everything |
125
|
6
|
50
|
|
|
|
18
|
(ref($classesref)) or croak 'usage: '.__PACKAGE__.'->classes_match(field, classesref))'; |
126
|
6
|
|
|
|
|
8
|
foreach (@{$classesref}) { |
|
6
|
|
|
|
|
14
|
|
127
|
6
|
50
|
|
|
|
16
|
return 1 if get_undef($self, $_); |
128
|
|
|
|
|
|
|
} |
129
|
0
|
|
|
|
|
0
|
return 0; |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
sub _classes_match_chooser { |
133
|
0
|
|
|
0
|
|
0
|
my $self = $_[0]; |
134
|
0
|
|
|
|
|
0
|
my $classesref = $_[1]; |
135
|
0
|
|
|
|
|
0
|
foreach (@{$classesref}) { |
|
0
|
|
|
|
|
0
|
|
136
|
0
|
0
|
|
|
|
0
|
return 1 if get_undef($self, $_); |
137
|
|
|
|
|
|
|
} |
138
|
0
|
|
|
|
|
0
|
return 0; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub eval_match { |
142
|
4
|
50
|
33
|
4
|
1
|
120
|
my $self = shift; ($self && ref($self)) or croak 'usage: '.__PACKAGE__.'->eval_match(subroutine)'; |
|
4
|
|
|
|
|
29
|
|
143
|
4
|
|
|
|
|
6
|
my $subref = shift; |
144
|
|
|
|
|
|
|
# @_ are optional arguments |
145
|
|
|
|
|
|
|
# See inlined version in host_match_chooser |
146
|
4
|
50
|
|
|
|
8
|
return 1 if !defined $subref; # Null reference means match everything |
147
|
4
|
|
|
|
|
10
|
return $self->_eval_generic_cb($subref,@_); |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub _eval_generic_cb { |
151
|
4
|
|
|
4
|
|
7
|
my $self = shift; |
152
|
4
|
|
|
|
|
4
|
my $subref = shift; |
153
|
|
|
|
|
|
|
# @_ are optional arguments |
154
|
|
|
|
|
|
|
# Call &$subref($self) in safe container |
155
|
4
|
|
|
|
|
34
|
return $Safer->eval_cb($subref,$self,@_); |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
###################################################################### |
159
|
|
|
|
|
|
|
#### Special accessors |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub cpus_slash { |
162
|
2
|
|
|
2
|
1
|
5
|
my $self = shift; |
163
|
2
|
50
|
|
|
|
27
|
if ($self->cpus != $self->physical_cpus) { |
164
|
0
|
|
|
|
|
0
|
return $self->physical_cpus."/".$self->cpus; |
165
|
|
|
|
|
|
|
} else { |
166
|
2
|
|
|
|
|
126
|
return $self->cpus; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub top_processes { |
171
|
2
|
50
|
33
|
2
|
1
|
3
|
my $self = shift; ($self && ref($self)) or croak 'usage: '.__PACKAGE__.'->key(key))'; |
|
2
|
|
|
|
|
19
|
|
172
|
2
|
|
|
|
|
3
|
my @keys = (values %{$self->{dynamic}{proc}}); |
|
2
|
|
|
|
|
8
|
|
173
|
2
|
|
|
|
|
4
|
grep {bless $_, 'Schedule::Load::Hosts::Proc'} @keys; |
|
1
|
|
|
|
|
19
|
|
174
|
|
|
|
|
|
|
#print "TOP PROC @keys\n"; |
175
|
2
|
50
|
|
|
|
13
|
return (wantarray ? @keys : \@keys); |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
sub holds { |
179
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
180
|
0
|
0
|
|
|
|
0
|
return if !$self->{dynamic}{holds}; |
181
|
0
|
|
|
|
|
0
|
return (sort {$a->compare_pri_time($b)} (@{$self->{dynamic}{holds}})); |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub free_cpus { |
185
|
8
|
|
|
8
|
0
|
15
|
my $self = shift; |
186
|
|
|
|
|
|
|
# How many more jobs host can take before we should turn off new jobs |
187
|
8
|
|
|
|
|
151
|
my $free = ($self->cpus - $self->adj_load); |
188
|
8
|
50
|
|
|
|
22
|
$free = 0 if ($free < 0); |
189
|
8
|
|
|
|
|
25
|
$free = int ($free + .7); |
190
|
8
|
|
|
|
|
25
|
return $free; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sub rating_cb { |
194
|
4
|
50
|
33
|
4
|
0
|
5
|
my $self = shift; ($self && ref($self)) or croak 'usage: '.__PACKAGE__.'->key(key))'; |
|
4
|
|
|
|
|
29
|
|
195
|
|
|
|
|
|
|
# How fast can we process a single job? |
196
|
|
|
|
|
|
|
# 0 indicates can't load this host |
197
|
|
|
|
|
|
|
# closer to 0 are the best ratings (as 'bad' is open-ended) |
198
|
4
|
50
|
33
|
|
|
11
|
if ($self->get_undef('load_limit') |
199
|
|
|
|
|
|
|
&& $self->load_limit <= $self->adj_load) { |
200
|
|
|
|
|
|
|
# Illegal to load this host more |
201
|
0
|
|
|
|
|
0
|
return 0; |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
4
|
|
|
|
|
6
|
my $rate = 1e9; |
205
|
|
|
|
|
|
|
# Multiply badness by cpu loading |
206
|
|
|
|
|
|
|
# Scale it to be between .8 and 1.0, else a large number of inactive jobs would |
207
|
|
|
|
|
|
|
# result in a very good rating, which would make that machine always be picked. |
208
|
4
|
|
|
|
|
82
|
$rate *= ((($self->total_pctcpu+1)/100) * 0.2 + 0.8); |
209
|
|
|
|
|
|
|
# Multiply that by number of jobs |
210
|
4
|
|
|
|
|
80
|
$rate *= ($self->adj_load+1); |
211
|
|
|
|
|
|
|
# Discount by cpus & frequency |
212
|
4
|
|
|
|
|
72
|
$rate /= $self->cpus; |
213
|
4
|
|
|
|
|
78
|
$rate /= $self->max_clock * 0.4; # 1 free cpu at 300Mhz beat 50% of a 600 Mhz cpu |
214
|
4
|
|
50
|
|
|
10
|
$rate *= ($self->get_undef('rating_mult') || 1.0); |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
#printf "%f * (%d+%d+1) / %f / %f = %f\n", ($self->total_pctcpu+1), $self->report_load, $self->adj_load, $self->cpus, $self->max_clock, $rate if $Debug; |
217
|
4
|
50
|
|
|
|
12
|
return 0 if $rate<=0; |
218
|
4
|
|
|
|
|
9
|
$rate = log($rate); # Make a more readable number |
219
|
4
|
|
50
|
|
|
9
|
$rate += ($self->get_undef('rating_adder') || 0); |
220
|
4
|
|
|
|
|
50
|
return $rate; |
221
|
|
|
|
|
|
|
} |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub rating { |
224
|
4
|
50
|
33
|
4
|
0
|
7
|
my $self = shift; ($self && ref($self)) or croak 'usage: '.__PACKAGE__.'->rating(subroutine)'; |
|
4
|
|
|
|
|
29
|
|
225
|
4
|
|
|
|
|
5
|
my $subref = shift; |
226
|
4
|
50
|
|
|
|
18
|
return $self->rating_cb() if !defined $subref; # Null reference means default callback |
227
|
0
|
|
|
|
|
0
|
return $self->_eval_generic_cb($subref); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
sub rating_chooser { |
231
|
|
|
|
|
|
|
# Similar to rating, but for internal use by the chooser - performance critical |
232
|
0
|
|
|
0
|
0
|
0
|
my $self = $_[0]; |
233
|
0
|
|
|
|
|
0
|
my $subref = $_[1]; |
234
|
0
|
|
|
|
|
0
|
my $scratchref = $_[2]; |
235
|
0
|
0
|
|
|
|
0
|
return $self->rating_cb() if !defined $subref; # Null reference means default callback |
236
|
0
|
|
|
|
|
0
|
return $self->_eval_generic_cb($subref, $scratchref); |
237
|
|
|
|
|
|
|
} |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub rating_text { |
240
|
2
|
50
|
33
|
2
|
0
|
5
|
my $self = shift; ($self && ref($self)) or croak 'usage: '.__PACKAGE__.'->rating(subroutine)'; |
|
2
|
|
|
|
|
23
|
|
241
|
2
|
50
|
|
|
|
44
|
return "inf" if $self->reserved; |
242
|
2
|
50
|
|
|
|
6
|
return "inf" if !$self->rating; |
243
|
2
|
50
|
|
|
|
4
|
return "slow" if $self->get_undef('slreportd_unresponsive'); |
244
|
2
|
|
|
|
|
6
|
return sprintf("%4.2f", $self->rating); |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
###################################################################### |
248
|
|
|
|
|
|
|
#### Accessors |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
sub AUTOLOAD { |
251
|
10
|
|
|
10
|
|
14
|
my $self = shift; |
252
|
10
|
50
|
|
|
|
26
|
my $type = ref($self) or croak "$self is not an ".__PACKAGE__." object"; |
253
|
|
|
|
|
|
|
|
254
|
10
|
|
|
|
|
50
|
(my $field = $AUTOLOAD) =~ s/.*://; # Remove package |
255
|
|
|
|
|
|
|
|
256
|
10
|
100
|
|
|
|
53
|
if (exists ($self->{dynamic}{$field})) { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
257
|
|
|
|
|
|
|
# Dynamic variables stay dynamic |
258
|
2
|
|
|
15
|
1
|
69
|
eval "sub $field { return \$_[0]->{dynamic}{$field}; }"; |
|
15
|
|
|
5
|
1
|
183
|
|
|
5
|
|
|
|
|
63
|
|
259
|
2
|
|
|
|
|
61
|
return $self->{dynamic}{$field}; |
260
|
|
|
|
|
|
|
} elsif (exists ($self->{stored}{$field})) { |
261
|
|
|
|
|
|
|
# Stored variables can move to/from const variables |
262
|
1
|
50
|
|
15
|
1
|
80
|
eval "sub $field { return (exists \$_[0]->{stored}{$field} " |
|
15
|
|
|
|
|
139
|
|
263
|
|
|
|
|
|
|
."? \$_[0]->{stored}{$field} : \$_[0]->{const}{$field}); }"; |
264
|
1
|
|
|
|
|
12
|
return $self->{stored}{$field}; |
265
|
|
|
|
|
|
|
} elsif (exists ($self->{const}{$field})) { |
266
|
7
|
50
|
|
1
|
1
|
602
|
eval "sub $field { return (exists \$_[0]->{stored}{$field} " |
|
1
|
50
|
|
25
|
1
|
24
|
|
|
25
|
50
|
|
142
|
1
|
295
|
|
|
142
|
50
|
|
5
|
1
|
1348
|
|
|
5
|
50
|
|
1
|
0
|
40
|
|
|
1
|
50
|
|
1
|
1
|
7
|
|
|
1
|
50
|
|
2
|
1
|
7
|
|
|
2
|
|
|
|
|
20
|
|
267
|
|
|
|
|
|
|
."? \$_[0]->{stored}{$field} : \$_[0]->{const}{$field}); }"; |
268
|
7
|
|
|
|
|
121
|
return $self->{const}{$field}; |
269
|
|
|
|
|
|
|
} else { |
270
|
0
|
|
|
|
|
0
|
croak "$type->$field: Unknown ".__PACKAGE__." field $field"; |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
0
|
|
|
0
|
|
|
sub DESTROY {} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
###################################################################### |
277
|
|
|
|
|
|
|
###################################################################### |
278
|
|
|
|
|
|
|
#### Package return |
279
|
|
|
|
|
|
|
1; |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
###################################################################### |
282
|
|
|
|
|
|
|
__END__ |