source: MondoRescue/branches/3.2/MondoRescue/lib/MondoRescue/LVM.pm@ 3432

Last change on this file since 3432 was 3432, checked in by Bruno Cornec, 9 years ago
  • Change analyze-my-lvm in order to generate percentage of Extents of a VG to use to create the LV instead of a fixed size. Should improve bugs when creating LVM on a disk of different size greatly.
File size: 18.7 KB
Line 
1#!/usr/bin/perl -w
2#
3# Subroutines related to LVM brought by the MondoRescue project
4#
5# $Id$
6#
7# Copyright B. Cornec 2008-2014
8# Provided under the GPL v2
9
10package MondoRescue::LVM;
11
12use strict 'vars';
13use Data::Dumper;
14use English;
15use lib qw (lib);
16use ProjectBuilder::Base;
17use ProjectBuilder::Conf;
18use MondoRescue::Base;
19
20# Inherit from the "Exporter" module which handles exporting functions.
21
22use Exporter;
23
24# Export, by default, all the functions into the namespace of
25# any code which uses this module.
26
27our @ISA = qw(Exporter);
28our @EXPORT = qw(mr_lvm_check mr_lvm_get_conf mr_lvm_read_conf mr_lvm_write_conf mr_lvm_edit_conf mr_lvm_apply_from_conf);
29
30=pod
31
32=head1 NAME
33
34MondoRescue::LVM, part of the mondorescue.org
35
36=head1 DESCRIPTION
37
38This modules provides low level functions for LVM support in the Mondorescue project
39
40=head1 USAGE
41
42=over 4
43
44=item B<mr_lvm_check>
45
46This function checks the usage of LVM and gets the version used
47It returns 2 parameters, the LVM version, and the lvm command to use if needed
48The LVM version could be undef, 0 (no LVM), 1 or 2 at the moment, or further not yet supported version
49It potentially takes one parameter, the LVM version, already known, in which case it easily deduced the LVM command.
50If LVM version is undefined then no LVM Handling should be done.
51It has to run on on a system where LVM is activated to return useful results so typically on the system to backup
52
53=cut
54
55sub mr_lvm_check {
56
57my $lvmver = shift;
58
59# Get params from the conf file
60my ($lvmds_t,$lvmproc_t,$lvmcmd_t,$lvmpath_t) = pb_conf_get("mr_cmd_lvmdiskscan","mr_proc_lvm","mr_cmd_lvm","mr_path_lvm");
61my $lvmds = $lvmds_t->{$ENV{PBPROJ}};
62my $lvmproc = $lvmproc_t->{$ENV{PBPROJ}};
63my $lvmcmd = $lvmcmd_t->{$ENV{PBPROJ}};
64my $lvmpath = $lvmpath_t->{$ENV{PBPROJ}};
65
66# That file is not mandatory anymore
67if (not defined $lvmver) {
68 if (! -x $lvmproc) {
69 pb_log(1,"$lvmproc doesn't exist\n");
70 } else {
71 # Check LVM volumes presence
72 pb_log(2,"Checking with $lvmproc\n");
73 open(LVM,$lvmproc) || mr_exit(-1,"Unable to open $lvmproc");
74 while (<LVM>) {
75 if (/0 VGs 0 PVs 0 LVs/) {
76 pb_log(1,"No LVM volumes found in $lvmproc\n");
77 return(0,undef);
78 }
79 }
80 close(LVM);
81 }
82}
83
84# Check LVM version
85if (not defined $lvmver) {
86 pb_log(2,"LVM version value is not known\n");
87 if (-x $lvmds) {
88 pb_log(2,"Checking with $lvmds\n");
89 open(LVM,"$lvmds --help 2>&1 |") || mr_exit(-1,"Unable to execute $lvmds");
90 while (<LVM>) {
91 if (/Logical Volume Manager/ || /LVM version:/) {
92 $lvmver = $_;
93 chomp($lvmver);
94 $lvmver =~ s/:([0-9])\..*/$1/;
95 }
96 }
97 close(LVM);
98 pb_log(2,"Found a LVM version of $lvmver with $lvmds --help\n") if (defined $lvmver);
99 }
100}
101
102if (not defined $lvmver) {
103 pb_log(2,"LVM version value is still not known\n");
104 if (-x $lvmcmd) {
105 pb_log(2,"Checking with $lvmcmd\n");
106 open(LVM,"$lvmcmd version |") || mr_exit(-1,"Unable to execute $lvmcmd");
107 while (<LVM>) {
108 if (/LVM version/) {
109 $lvmver = $_;
110 chomp($lvmver);
111 $lvmver =~ s/:([0-9])\..*/$1/;
112 $lvmver =~ s/[\s]*LVM version[:]*[\s]+([0-9])\..*/$1/;
113 }
114 }
115 close(LVM);
116 pb_log(2,"Found a LVM version of $lvmver with $lvmcmd version\n") if (defined $lvmver);
117 }
118}
119
120if (not defined $lvmver) {
121 # Still not found
122 mr_log(0,"Unable to determine LVM version.\nIf you think this is wrong, please report to the dev team with the result of the commands:\n$lvmds --help and $lvmcmd version\n");
123} elsif ($lvmver == 1) {
124 $lvmcmd = "$lvmpath";
125} elsif ($lvmver == 2) {
126 $lvmcmd .= " ";
127} else {
128 pb_log(0,"Unknown LVM version $lvmver\n");
129}
130# Here $lvmcmd contains a full path name
131pb_log(1,"Found LVM version $lvmver\n");
132return ($lvmver,$lvmcmd);
133
134}
135
136=over 4
137
138=item B<mr_lvm_get_conf>
139
140This function returns 1 parameters, the LVM structure or undef if no LVM
141That LVM structure contains all the information related to the current LVM configuration
142
143=cut
144
145sub mr_lvm_get_conf {
146
147my $lvm = undef;
148
149my ($lvmver,$lvmcmd) = mr_lvm_check();
150return(undef) if ((not defined $lvmver) || ($lvmver == 0));
151
152# Analyze the existing physical volumes
153open(LVM,$lvmcmd."pvs --noheadings --nosuffix --units m --separator : -o pv_name,vg_name,pv_all,pv_fmt,pv_uuid,dev_size,pv_mda_free,pv_mda_size |") || mr_exit(-1,"Unable to execute ".$lvmcmd."pvs");
154while (<LVM>) {
155 s/^[\s]*//;
156
157 my ($pv_name,$vg_name,$pe_start,$pv_size,$pv_free,$pv_used,$pv_attr,$pv_pe_count,$pv_pe_alloc_count,$pv_tags,$pv_mda_count,$pv_uuid,$dev_size,$pv_mda_free,$pv_mda_size) = split(/:/);
158
159=pod
160
161The LVM hash is indexed by VGs, provided by the vg_name attribute of the pvs command
162
163vg_name - Name of the volume group linked to this PV
164
165=cut
166
167 $lvm->{$vg_name}->{'pvnum'}++;
168
169=pod
170
171The structure contains an array of PVs called pvs and starting at 1, containing the name of the PV as provided by the pv_name attribute of the pvs command
172
173pv_name - Name of the physical volume PV
174
175=cut
176
177 # Array of PVs for that VG
178 $lvm->{$vg_name}->{'pvs'}->[$lvm->{$vg_name}->{'pvnum'}] = $pv_name;
179
180=pod
181
182All the PV fields from the pvs command are gathered under their PV name (substructure)
183The following names are used:
184
185From pvs -o help
186pe_start - Offset to the start of data on the underlying device.
187pv_size - Size of PV in current units.
188pv_free - Total amount of unallocated space in current units.
189pv_used - Total amount of allocated space in current units.
190pv_attr - Various attributes - see man page.
191pv_pe_count - Total number of Physical Extents.
192pv_pe_alloc_count - Total number of allocated Physical Extents.
193pv_tags - Tags, if any.
194pv_mda_count - Number of metadata areas on this device.
195pv_fmt - Type of metadata.
196pv_uuid - Unique identifier.
197dev_size - Size of underlying device in current units.
198pv_mda_free - Free metadata area space on this device in current units.
199pv_mda_size - Size of smallest metadata area on this device in current units.
200
201=cut
202
203 $lvm->{$vg_name}->{$pv_name}->{'pe_start'} = $pe_start;
204 $lvm->{$vg_name}->{$pv_name}->{'pv_size'} = $pv_size;
205 $lvm->{$vg_name}->{$pv_name}->{'pv_free'} = $pv_free;
206 $lvm->{$vg_name}->{$pv_name}->{'pv_used'} = $pv_used;
207 $lvm->{$vg_name}->{$pv_name}->{'pv_attr'} = $pv_attr;
208 $lvm->{$vg_name}->{$pv_name}->{'pv_pe_count'} = $pv_pe_count;
209 $lvm->{$vg_name}->{$pv_name}->{'pv_pe_alloc_count'} = $pv_pe_alloc_count;
210 $lvm->{$vg_name}->{$pv_name}->{'pv_tags'} = $pv_tags;
211 $lvm->{$vg_name}->{$pv_name}->{'pv_mda_count'} = $pv_mda_count;
212 $lvm->{$vg_name}->{$pv_name}->{'pv_uuid'} = $pv_uuid;
213 $lvm->{$vg_name}->{$pv_name}->{'dev_size'} = $dev_size;
214 $lvm->{$vg_name}->{$pv_name}->{'pv_mda_free'} = $pv_mda_free;
215 $lvm->{$vg_name}->{$pv_name}->{'pv_mda_size'} = $pv_mda_size;
216}
217close(LVM);
218
219# Analyze the existing volume groups
220#open(LVM,$lvmcmd."vgdisplay -c |") || mr_exit(-1,"Unable to execute ".$lvmcmd."vgdisplay -c");
221open(LVM,$lvmcmd."vgs --noheadings --nosuffix --units m --separator : -o vg_all |") || mr_exit(-1,"Unable to execute ".$lvmcmd."vgs");
222while (<LVM>) {
223
224=pod
225
226All the VG fields from the vgs command are gathered under the VG name
227The following names are used:
228
229From vgs -o help
230vg_fmt - Type of metadata.
231vg_uuid - Unique identifier.
232vg_attr - Various attributes - see man page.
233vg_size - Total size of VG in current units.
234vg_free - Total amount of free space in current units.
235vg_sysid - System ID indicating when and where it was created.
236vg_extent_size - Size of Physical Extents in current units.
237vg_extent_count - Total number of Physical Extents.
238vg_free_count - Total number of unallocated Physical Extents.
239max_lv - Maximum number of LVs allowed in VG or 0 if unlimited.
240max_pv - Maximum number of PVs allowed in VG or 0 if unlimited.
241pv_count - Number of PVs.
242lv_count - Number of LVs.
243snap_count - Number of snapshots.
244vg_seqno - Revision number of internal metadata. Incremented whenever it changes.
245vg_tags - Tags, if any.
246vg_mda_count - Number of metadata areas in use by this VG.
247vg_mda_free - Free metadata area space for this VG in current units.
248vg_mda_size - Size of smallest metadata area for this VG in current units.
249
250=cut
251 s/^[\s]*//;
252 my ($vg_fmt,$vg_uuid,$vg_name,$vg_attr,$vg_size,$vg_free,$vg_sysid,$vg_extend_size,$vg_extend_count,$vg_free_count,$max_lv,$max_pv,$pv_count,$lv_count,$snap_count,$vg_seqno,$vg_tags,$vg_mda_count,$vg_mda_free,$vg_mda_size) = split(/:/);
253 $lvm->{$vg_name}->{'vg_fmt'} = $vg_fmt;
254 $lvm->{$vg_name}->{'vg_uuid'} = $vg_uuid;
255 $lvm->{$vg_name}->{'vg_attr'} = $vg_attr;
256 $lvm->{$vg_name}->{'vg_size'} = $vg_size;
257 $lvm->{$vg_name}->{'vg_free'} = $vg_free;
258 $lvm->{$vg_name}->{'vg_sysid'} = $vg_sysid;
259 $lvm->{$vg_name}->{'vg_extend_size'} = $vg_extend_size;
260 $lvm->{$vg_name}->{'vg_extend_count'} = $vg_extend_count;
261 $lvm->{$vg_name}->{'vg_free_count'} = $vg_free_count;
262 $lvm->{$vg_name}->{'max_lv'} = $max_lv;
263 $lvm->{$vg_name}->{'max_pv'} = $max_pv;
264 $lvm->{$vg_name}->{'pv_count'} = $pv_count;
265 $lvm->{$vg_name}->{'lv_count'} = $lv_count;
266 $lvm->{$vg_name}->{'snap_count'} = $snap_count;
267 $lvm->{$vg_name}->{'vg_seqno'} = $vg_seqno;
268 $lvm->{$vg_name}->{'vg_tags'} = $vg_tags;
269 $lvm->{$vg_name}->{'vg_mda_count'} = $vg_mda_count;
270 $lvm->{$vg_name}->{'vg_mda_free'} = $vg_mda_free;
271 $lvm->{$vg_name}->{'vg_mda_size'} = $vg_mda_size;
272}
273close(LVM);
274
275# Analyze the existing logical volumes
276#open(LVM,$lvmcmd."lvdisplay -c |") || mr_exit(-1,"Unable to execute ".$lvmcmd."lvdisplay -c");
277open(LVM,$lvmcmd."lvs --noheadings --nosuffix --units m --separator : -o vg_name,lv_all|") || mr_exit(-1,"Unable to execute ".$lvmcmd."lvs");
278while (<LVM>) {
279 s/^[\s]*//;
280
281=pod
282
283The structure contains an array of LVs called lvs and starting at 1, containing the name of the PV as provided by the pv_name attribute of the pvs command
284
285=cut
286
287 my ($vg_name,$lv_uuid,$lv_name,$lv_attr,$lv_major,$lv_minor,$lv_read_ahead,$lv_kernel_major,$lv_kernel_minor,$lv_kernel_read_ahead,$lv_size,$seg_count,$origin,$origin_size,$snap_percent,$copy_percent,$move_pv,$convert_lv,$lv_tags,$mirror_log,$modules) = split(/:/);
288
289=pod
290
291All the PV fields from the pvs command are gathered under their PV name (substructure)
292The following names are used:
293
294
295 # From lvs -o help
296
297 #vg_name - Name of the related volume group
298 #lv_uuid - Unique identifier.
299 #lv_name - Name. LVs created for internal use are enclosed in brackets.
300 #lv_attr - Various attributes - see man page.
301 #lv_major - Persistent major number or -1 if not persistent.
302 #lv_minor - Persistent minor number or -1 if not persistent.
303 #lv_read_ahead - Read ahead setting in current units.
304 #lv_kernel_major - Currently assigned major number or -1 if LV is not active.
305 #lv_kernel_minor - Currently assigned minor number or -1 if LV is not active.
306 #lv_kernel_read_ahead - Currently-in-use read ahead setting in current units.
307 #lv_size - Size of LV in current units.
308 #seg_count - Number of segments in LV.
309 #origin - For snapshots, the origin device of this LV.
310 #origin_size - For snapshots, the size of the origin device of this LV.
311 #snap_percent - For snapshots, the percentage full if LV is active.
312 #copy_percent - For mirrors and pvmove, current percentage in-sync.
313 #move_pv - For pvmove, Source PV of temporary LV created by pvmove.
314 #convert_lv - For lvconvert, Name of temporary LV created by lvconvert.
315 #lv_tags - Tags, if any.
316 #mirror_log - For mirrors, the LV holding the synchronisation log.
317 #modules - Kernel device-mapper modules required for this LV.
318
319=cut
320
321 # The LVM hash is indexed by VGs
322 $lvm->{$vg_name}->{'lvnum'}++;
323 # That array will start at 1 then
324 # Array of LVs for that VG
325 $lvm->{$vg_name}->{'lvs'}->[$lvm->{$vg_name}->{'lvnum'}] = $lv_name;
326 # All LV fields gathered under the LV name
327 $lvm->{$vg_name}->{$lv_name}->{'lv_uuid'} = $lv_uuid;
328 $lvm->{$vg_name}->{$lv_name}->{'lv_attr'} = $lv_attr;
329 $lvm->{$vg_name}->{$lv_name}->{'lv_major'} = $lv_major;
330 $lvm->{$vg_name}->{$lv_name}->{'lv_minor'} = $lv_minor;
331 $lvm->{$vg_name}->{$lv_name}->{'lv_read_ahead'} = $lv_read_ahead;
332 $lvm->{$vg_name}->{$lv_name}->{'lv_kernel_major'} = $lv_kernel_major;
333 $lvm->{$vg_name}->{$lv_name}->{'lv_kernel_minor'} = $lv_kernel_minor;
334 $lvm->{$vg_name}->{$lv_name}->{'lv_kernel_read_ahead'} = $lv_kernel_read_ahead;
335 $lvm->{$vg_name}->{$lv_name}->{'lv_size'} = $lv_size;
336 $lvm->{$vg_name}->{$lv_name}->{'origin'} = $origin;
337 $lvm->{$vg_name}->{$lv_name}->{'origin_size'} = $origin_size;
338 $lvm->{$vg_name}->{$lv_name}->{'snap_percent'} = $snap_percent;
339 $lvm->{$vg_name}->{$lv_name}->{'copy_percent'} = $copy_percent;
340 $lvm->{$vg_name}->{$lv_name}->{'move_pv'} = $move_pv;
341 $lvm->{$vg_name}->{$lv_name}->{'convert_lv'} = $convert_lv;
342 $lvm->{$vg_name}->{$lv_name}->{'lv_tags'} = $lv_tags;
343 $lvm->{$vg_name}->{$lv_name}->{'mirror_log'} = $mirror_log;
344 $lvm->{$vg_name}->{$lv_name}->{'modules'} = $modules;
345}
346close(LVM);
347return($lvm);
348}
349
350=item B<mr_lvm_analyze>
351
352This function outputs in a file descriptor the LVM analysis done
353It returns 1 parameters, the LVM version or 0 if no LVM
354
355=cut
356
357sub mr_lvm_analyze {
358
359my $OUTPUT = shift;
360
361my ($lvmver,$lvmcmd) = mr_lvm_check();
362my $lvm = mr_lvm_get_conf();
363return(undef) if ($lvmver == 0);
364
365print $OUTPUT "LVM:$lvmver\n";
366
367# Analyze the existing physical volumes
368#open(LVM,$lvmcmd."pvdisplay -c |") || mr_exit(-1,"Unable to execute ".$lvmcmd."pvdisplay -c");
369open(LVM,$lvmcmd."pvs --noheadings --nosuffix --units m --separator : |") || mr_exit(-1,"Unable to execute ".$lvmcmd."pvs");
370while (<LVM>) {
371 s/^[\s]*//;
372 my ($pv,$vg,$foo,$foo2,$size,$foo3) = split(/:/);
373 $lvm->{$vg}->{'pvnum'}++;
374 # that array will start at 1 then
375 $lvm->{$vg}->{'pv'}->[$lvm->{$vg}->{'pvnum'}] = $pv;
376 $lvm->{$vg}->{$pv}->{'size'} = $size;
377 print $OUTPUT "PV:$_";
378}
379close(LVM);
380
381# Analyze the existing volume groups
382#open(LVM,$lvmcmd."vgdisplay -c |") || mr_exit(-1,"Unable to execute ".$lvmcmd."vgdisplay -c");
383open(LVM,$lvmcmd."vgs --noheadings --nosuffix --units m --separator : |") || mr_exit(-1,"Unable to execute ".$lvmcmd."vgs");
384while (<LVM>) {
385 s/^[\s]*//;
386 print $OUTPUT "VG:$_";
387}
388close(LVM);
389
390# Analyze the existing logical volumes
391#open(LVM,$lvmcmd."lvdisplay -c |") || mr_exit(-1,"Unable to execute ".$lvmcmd."lvdisplay -c");
392open(LVM,$lvmcmd."lvs --noheadings --nosuffix --units m --separator : |") || mr_exit(-1,"Unable to execute ".$lvmcmd."lvs");
393while (<LVM>) {
394 s/^[\s]*//;
395 print $OUTPUT "LV:$_";
396}
397close(LVM);
398return($lvm);
399}
400
401
402=over 4
403
404=item B<mr_lvm_prepare>
405
406This function outputs in a file descriptor the LVM setup needed to restore LVM conf
407It returns 1 parameters, the LVM version or 0 if no LVM
408
409=cut
410
411sub mr_lvm_prepare {
412
413my $INPUT = shift;
414my $OUTPUT = shift;
415my $mrmult = shift;
416my $lvmcmd;
417my $lvmver;
418
419# Generate the startup scrit needed to restore LVM conf
420# from what is given on input
421# Multiply by the multiplier given in input or 1 of none
422
423my $firsttime = 0;
424while (<$INPUT>) {
425 if (/^LVM:/) {
426 my $tag;
427 my $foo;
428 ($tag,$lvmver) = split(/:/);
429 ($foo,$lvmcmd) = mr_lvm_check($lvmver);
430
431 print $OUTPUT "# Desactivate Volume Groups\n";
432 print $OUTPUT $lvmcmd."vgchange -an\n";
433 print $OUTPUT "\n";
434
435 } elsif (/^PV:/) {
436 # This is for pvdisplay -c
437 #my ($tag,$pvname,$vgname,$pvsize,$ipvn,$pvstat,$pvna,$lvnum,$pesize,$petot,$pefree,$pelloc) = split(/:/);
438 my ($tag,$pvname,$vgname,$lvmv,$more,$pesize,$pefree) = split(/:/);
439 print $OUTPUT "# Creating Physical Volumes $pvname\n";
440 print $OUTPUT $lvmcmd."pvcreate -ff -y";
441 print $OUTPUT " -s ".$pesize*$mrmult if (defined $pesize);
442 print $OUTPUT " $pvname\n";
443 print $OUTPUT "\n";
444 } elsif (/^VG:/) {
445 # This if for vgdisplay -c
446 #my ($tag,$vgname,$vgaccess,$vgstat,$vgnum,$lvmaxnum,$lvnum,$ocalvinvg,$lvmaxsize,$pvmaxnum,$cnumpv,$anumpv,$vgsize,$pesize,$penum,$pealloc,$pefree,$uuid) = split(/:/);
447 my ($tag,$vgname,$pvnum,$lvnum,$attr,$vgsize,$vgfree) = split(/:/);
448 if ($lvmver < 2) {
449 print $OUTPUT "# Removing device first as LVM v1 doesn't do it\n";
450 print $OUTPUT "rm -Rf /dev/$vgname\n";
451 }
452 #$lvmaxnum = 255 if (($lvmaxnum > 256) or (not defined $lvmaxnum));
453 #$pvmaxnum = 255 if (($pvmaxnum > 256) or (not defined $pvmaxnum));
454 print $OUTPUT "# Create Volume Group $vgname\n";
455 # Pb sur pesize unite ?
456 print $OUTPUT $lvmcmd."vgcreate $vgname ";
457 #print $OUTPUT "-p $pvmaxnum -l $lvmaxnum";
458 #print $OUTPUT " -s ".$pesize."\n" if (defined $pesize);
459 print $OUTPUT "\n";
460
461 } elsif (/^LV:/) {
462 if ($firsttime == 0) {
463 print $OUTPUT "\n";
464 print $OUTPUT "# Activate All Volume Groups\n";
465 print $OUTPUT $lvmcmd."vgchange -ay\n";
466 print $OUTPUT "\n";
467 $firsttime = 1;
468 }
469 my ($tag,$lvname,$vgname,$lvaccess,$lvstat,$lvnum,$oclv,$lvsize,$leinlv,$lealloc,$allocpol,$readahead,$major,$minor) = split(/:/);
470 print $OUTPUT "# Create Logical Volume $lvname\n";
471 print $OUTPUT $lvmcmd."echo y | lvcreate -n $lvname -L ".$lvsize*$mrmult;
472 print $OUTPUT " -r $readahead" if (defined $readahead);
473 print $OUTPUT " $vgname\n";
474 #[ "$stripes" ] && output="$output -i $stripes"
475 #[ "$stripesize" ] && output="$output -I $stripesize"
476 }
477}
478
479print $OUTPUT "\n";
480print $OUTPUT "# Scanning again Volume Groups\n";
481print $OUTPUT $lvmcmd."vgscan\n";
482print $OUTPUT "\n";
483
484}
485
486=back
487
488=head1 WEB SITES
489
490The main Web site of the project is available at L<http://www.mondorescue.org/>. Bug reports should be filled using the trac instance of the project at L<http://trac.mondorescue.org/>.
491
492=head1 USER MAILING LIST
493
494The mailing list of the project is available at L<mailto:mondo@lists.sf.net>
495
496=head1 AUTHORS
497
498The Mondorescue.org team L<http://www.mondorescue.org/> lead by Bruno Cornec L<mailto:bruno@mondorescue.org>.
499
500=head1 COPYRIGHT
501
502This module is distributed under the GPL v2.0 license
503described in the file C<COPYING> included with the distribution.
504
505
506=cut
507
5081;
509
Note: See TracBrowser for help on using the repository browser.