diff --git a/README.md b/README.md
index c2c9d7f29c05dc00c424eebe4f66f5377387ead6..854eda9296a8651f81a7e1c2998111515ea946c8 100644
--- a/README.md
+++ b/README.md
@@ -1,92 +1,47 @@
-# hac
+# has and hac - hpssadm wrapper
 
-hpssadm wrapper
+'hac and has' are developed by MAX PLANCK COMPUTING AND DATA FACILITY and published under GNU GENERAL PUBLIC LICENSE Version 3.
+For questions and suggestions please contact Elena Summer elena.summer (at) mpcdf.mpg.de
 
-## Getting started
+'hac and has' is a client-server application. 
+'has' is a server part. It takes care of starting and running hpssadm and waits for client commands. 
+'hac' is a client part. It translates simple 'hac' commands to complicated hpssadm commands and sends them to 'has'.
+'hac.dict' is used by 'hac' to translate 'hac' commands into hpssadm commands.
 
-To make it easy for you to get started with GitLab, here's a list of recommended next steps.
+MAX PLANCK COMPUTING AND DATA FACILITY has no responsibility for data damage or data loss when using this software.
+Try it first on a test system to be sure it behaves appropriately.
 
-Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
+## Installation ##
 
-## Add your files
+Copy 'hac', 'has' and 'hac.dict' in a directory which should be in PATH, for example /usr/local/bin/.
 
-- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
-- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
+Create a 'has' systemctl service. Start 'has' with 'systemctl start has'. Then use 'hac' commands. See 'hac help' for details.
 
-```
-cd existing_repo
-git remote add origin https://gitlab.mpcdf.mpg.de/mpcdf/backup-and-archive/hac.git
-git branch -M main
-git push -uf origin main
-```
+systemctl service is used by 'hac' to restart 'has' if it is not running. Therefore, you should either create the 'has' systemctl service or change 'hac' code to not use it. If you don't use a 'has' systemctl service, you can start 'has' by just typing "has" in terminal.
 
-## Integrate with your tools
+'hac' and 'has' log files as well as hac.dict are located in the same directory where the executables - 'hac' and 'has' - reside . Please change the code to use different directories.
 
-- [ ] [Set up project integrations](https://gitlab.mpcdf.mpg.de/mpcdf/backup-and-archive/hac/-/settings/integrations)
+hpssadm is called by 'has' via "/opt/hpss/bin/hpssadm.pl -b -U <USER> -a <KEYTAB> -j /usr/bin 2>&1". Change it in 'has' code if you call it differently. Place user name and keytab file location. Correct the java location if it differs from "/usr/bin".
 
-## Collaborate with your team
+In 'hac', 'has', and 'hac.dict' code, look for '# ATTENTION:'. Check and change these parts of code if necessary.
 
-- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
-- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
-- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
-- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
-- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
 
-## Test and Deploy
+## Prerequisitives ##
 
-Use the built-in continuous integration in GitLab.
+- 'hac' works with HPSS version 7.5.3 and 9.2.0. It may work with other HPSS versions but we have not tested it. 
+- perl
+- hpssadm
 
-- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
-- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
-- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
-- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
-- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
+## Implementing new hac commands ##
+Add new commands to hac.dict.
 
-***
+## License ## 
+'hac and has' is published under GNU GENERAL PUBLIC LICENSE Version 3.
 
-# Editing this README
+## Contributing ##
+If you wish to contribute to this project, please, first, crete your own branch and do your changes there. After you are ready create a merge request in Gitlab with 'develop' as a target branch - here are instructions: https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html .
 
-When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!).  Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
+Please be aware that your changes will be published under GNU GENERAL PUBLIC LICENSE Version 3.
 
-## Suggestions for a good README
-Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
 
-## Name
-Choose a self-explaining name for your project.
 
-## Description
-Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
-
-## Badges
-On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
-
-## Visuals
-Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
-
-## Installation
-Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
-
-## Usage
-Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
-
-## Support
-Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
-
-## Roadmap
-If you have ideas for releases in the future, it is a good idea to list them in the README.
-
-## Contributing
-State if you are open to contributions and what your requirements are for accepting them.
-
-For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
-
-You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
-
-## Authors and acknowledgment
-Show your appreciation to those who have contributed to the project.
-
-## License
-For open source projects, say how it is licensed.
-
-## Project status
-If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
diff --git a/hac b/hac
new file mode 100755
index 0000000000000000000000000000000000000000..5c0b7261c3b8511bbd3207360a5a2085d28035a0
--- /dev/null
+++ b/hac
@@ -0,0 +1,690 @@
+#!/usr/bin/perl
+
+#
+# This file is part of has and hac - hpssadm wrapper
+#
+# Copyright (C) 2022 MPCDF
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+use strict;
+use warnings;
+
+use IO::Socket;
+use Cwd;
+use POSIX;
+use File::Spec;
+use Text::Wrap;
+
+use FindBin;
+use lib $FindBin::Bin;
+
+my $VERSION = 3.1;
+
+# When TESTRUN is ON, only the test 
+# "Going to send hpssadm_command = hpssadm_command" is printed
+# no hpssadm is called
+my $TESTRUN = 0;
+
+my $CMDSRV = "localhost";
+
+# ATTENTION:
+# change port if neccessary, should be in sync with has
+my $TCPPORT = 9090;
+
+# ATTENTION:
+# the log file has.log will be put in the directory where th executable 'hac' is
+# if needed, put different directory here
+my $LOGF = $FindBin::Bin . "/hac.log";
+
+# ATTENTION:
+# the dictionary hac.dict is in the directory where th executable 'hac' is
+# if needed, put different directory here
+my $DICTIONARY = $FindBin::Bin . "/hac.dict";
+
+# ATTENTION:
+# put your libararies' names here
+# hac lib argument option => PVR name
+# PVR names can be found, for example, in HPSS GUI 'Import Tape Volumes' window
+my %PVR_NAME = ('lib1' => 'LIB1_NAME',
+                'lib2' => 'LIB2_NAME');
+
+# ATTENTION:
+# change or add media types depending on which types you use
+# PVR names can be found, for example, in HPSS GUI 'Import Tape Volumes' window
+my %MEDIA_TYPE = (  'lto5' => 'IBM - 3580 (LTO) Gen5 Tape',
+                    'lto6' => 'IBM - 3580 (LTO) Gen6 Tape',
+                    'lto7' => 'IBM - 3580 (LTO) Gen7 Tape',
+                    'lto7m8' => 'IBM - 3580 (LTO) Gen8 Type M Tape',
+                    'lto8' => 'IBM - 3580 (LTO) Gen8 Tape');
+
+my %IMPORT_TYPE = ( 'def' => 'Default Import',
+                    'sc' => 'Scratch Import',
+                    'ow' => 'Overwrite Import');
+
+my %conf;
+my %dict;
+my %dict_help;
+
+$| = 1;
+
+log_start();
+read_config();
+read_dictionary();
+service_request();
+
+exit(0);
+
+###############################################################################
+sub read_config {
+    open( DATA, $DICTIONARY ) or die "Couldn't open file $DICTIONARY, $!";
+    my @lines = <DATA>;
+    close( DATA );
+
+    for my $line (@lines) {
+        next if ( index( $line, "#" ) == 0 );
+        my @line_array = split( ':', $line );
+        if ( @line_array == 2 ) {
+            my $l1 = $line_array[1];
+            chop $l1;
+            $conf{ $line_array[0] } = $l1;
+        }
+    }
+}
+
+###############################################################################
+sub read_dictionary {
+    open( DATA, $DICTIONARY ) or die "Couldn't open file $DICTIONARY, $!";
+    my @lines = <DATA>;
+    close( DATA );
+
+    for my $line (@lines) {
+        next if ( index( $line, "#" ) == 0 ); 
+        my @line_array = split( ':', $line );
+        if ( @line_array == 4 ) {
+            $dict_help{ $line_array[0] } = $line_array[3];
+            my $regex = $line_array[1];
+            foreach my $key ( keys %conf ) {
+                my $value = $conf{ $key };
+                $regex =~ s/$key/$value/g;
+            }
+            $dict{ $regex } = $line_array[2]; 
+        }    
+    }
+}
+
+###########################################################################
+sub service_request {
+    my $request = join( ' ', @ARGV );
+
+    if ( ( !( defined $request ) ) || '' eq $request ) {
+        print_usage(); 
+        exit(1); 
+    } elsif ( $request eq 'help' || $request eq '-h' || $request eq '--help' ) {
+        print_help();
+    } elsif ( $request eq 'version' || $request eq '-v' || $request eq '--version' ) {
+        print "$VERSION\n";
+    } elsif ( ( index( $request, 'help ') == 0 || index( $request, '-h ') == 0 || index( $request, '--help ') == 0 ) ) {
+        my @req_array = split( ' ', $request );
+        shift @req_array;
+        print_usage( join (' ', @req_array ) );
+    } elsif ( $request =~ qr/^.*list mount tapes.*$/ ) {
+        list_mount_tapes( @ARGV );
+    } elsif ( $request =~ qr/^.*list disks.*$/ ) {
+        list_disks( @ARGV );
+    } elsif ( check_arguments( $request ) ) {
+        lprint( "Error: Requested command $request not found in dictionary\n" );
+        print STDERR "Incorrect command: \"$request\". Refer to 'hac help' for usage.\n"; 
+        exit(1);    
+    }
+    exit(0);
+}
+
+###########################################################################
+sub check_arguments {
+    my $request = shift;
+    $request =~ s/devid/dev/g;
+    $request =~ s/drid/drive/g;
+
+    my $create_disk = ( $request =~qr/^create disk .*$/ ) ? 1 : 0;
+    my $delete_disk = ( $request =~qr/^del disk .*$/ ) ? 1 : 0;
+    my $lock_drive = ( $request =~qr/^(un)?lock drive .*$/ ) ? 1 : 0;
+    my $rep_reset_dev = ( $request =~qr/^rep reset dev .*$/ ) ? 1 : 0;
+    my $file = undef;
+
+    if ( index( $request, ' -t') != -1 ) {
+        $TESTRUN = 1;
+        $request =~ s/\ -t//g;
+    }
+
+    foreach my $pattern ( keys %dict ) {
+        next if ( index( $pattern, "#" ) == 0 );
+        my $regex = qr/^${pattern}$/;
+        if ( my @matches = $request =~ $regex ) {
+            lprint( "Requested command '$request' matches pattern '$pattern'\n" );
+            my $hpssadm_command = $dict{ $pattern };
+            my @volumes = ();
+            my @capacity = ();
+            foreach my $match ( @matches ) {
+                next unless ( ( defined $match ) && !( $match eq '' ) );
+                my @pair = split( ' ', $match );
+                next unless ( @pair > 1 );
+                my $key = uc( $pair[0] );
+                my $value = $pair[1];
+                for my $i ( 2..@pair-1 ) {
+                    $value = $value . ' ' . $pair[$i];                    
+                }
+                my $checked_value = check_value($key, $value);
+
+                if ( $key eq 'VOL' || $key eq 'FILE' ) {
+                    $file = $checked_value if ( $key eq 'FILE' );
+                    @volumes = build_volume_list( $key, $checked_value );
+                    if ( @volumes == 0 ) {
+                        print "Cannot build list of volumes out of $checked_value\n";
+                        exit(1);                 
+                    }
+                }
+
+                if ( $lock_drive ) {
+                    if ( $checked_value =~qr/^\w{6}.*$/ ) {
+                        my @vols = build_volume_list( 'VOL', $checked_value );
+                        my @dev_ids = ();
+                        foreach my $vol ( @vols ) {
+                            my $dev_id = get_dev_id_from_volume( $vol );
+                            push @dev_ids, $dev_id;
+                        }
+                        $checked_value = join( ',', @dev_ids );
+                    }
+                }
+
+                if ( $rep_reset_dev && $key eq 'DEV' ) {
+                    reset_bytes( $checked_value );
+                }
+
+                $hpssadm_command =~ s/\[$key\]/$checked_value/g unless ( $key eq 'VOL' );
+            }
+         
+            if ( @volumes == 0 ) {
+                print( "Going to send hpssadm_command = $hpssadm_command\n" ) if ( $TESTRUN );
+                print process_valid_request( $hpssadm_command ) unless ( $TESTRUN );
+            } elsif ( $create_disk ) {
+                foreach my $volume ( @volumes ) {
+                    my $hc = $hpssadm_command;
+                    $hc =~ s/-field \"Use list in external file\" -subfield \"File\" /-field \"Use starting label\" -subfield \"Volume Label\" /g;
+                    $hc =~ s/\"$file\"/\[VOL\]/g if ( defined $file );
+                    $hc =~ s/\[VOL\]/$volume/g;
+                    my $dev_id = get_dev_id_from_volume( $volume );
+                    my @hac_output = get_dev_info( $dev_id ); 
+                    my $ca = extract_capacity( $dev_id, @hac_output );
+                    $hc =~ s/\[SIZE\]/$ca/g;
+                    print( "Going to send hpssadm_command = $hc\n" ) if ( $TESTRUN );
+                    print process_valid_request( $hc ) unless ( $TESTRUN );
+                }
+                print_n_tasks( scalar @volumes )  unless ( $TESTRUN );
+       
+            } elsif ( !defined $file ) {
+                my $first_volume = shift @volumes;
+                if ( $delete_disk ) {
+                    my $dev_id_first_volume = get_dev_id_from_volume( $first_volume );
+                    reset_bytes( $dev_id_first_volume );
+                }
+                $hpssadm_command =~ s/\[VOL\]/$first_volume/g;
+                foreach my $volume ( @volumes ) {
+                    if ( $delete_disk ) {
+                        my $dev_id = get_dev_id_from_volume( $volume );
+                        reset_bytes( $dev_id );
+                    }
+                    if ( index ( $hpssadm_command, " -field \"Use starting label\"" ) != -1 ) {
+                        $hpssadm_command = $hpssadm_command . " -field \"Use starting label\" -subfield \"Volume Label\" $volume";
+                    } elsif ( index ( $hpssadm_command, " -field \"Build List of Volumes\"" ) != -1 ) {
+                        $hpssadm_command = $hpssadm_command . " -field \"Build List of Volumes\" -subfield \"Use starting label\" -subfield \"Volume Label\" $volume";
+                    } elsif ( index ( $hpssadm_command, " -field \"Import Information\"" ) != -1  ) {
+                        $hpssadm_command = $hpssadm_command . " -field \"Import Information\" -subfield \"Build List of Volumes\" -subfield \"Use starting label\" -subfield \"Volume Label\" $volume";
+                    }
+                }
+                print( "Going to send hpssadm_command = $hpssadm_command\n" ) if ( $TESTRUN );
+                print process_valid_request( $hpssadm_command ) unless ( $TESTRUN );
+                print_n_tasks(1)  unless ( $TESTRUN );
+            } elsif ( defined $file ) {
+                print( "Going to send hpssadm_command = $hpssadm_command\n" ) if ( $TESTRUN );
+                print process_valid_request( $hpssadm_command ) unless ( $TESTRUN );
+            }
+
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+###########################################################################
+sub check_value {
+    my $key = shift;
+    my $value = shift;
+
+    my $result = $value;
+
+    if ( 'MT' eq $key ) {
+       $result = $MEDIA_TYPE{ $value };
+    } elsif ( 'PVR' eq $key ) {
+       $result = $PVR_NAME{ $value };
+    } elsif ( 'IT' eq $key ) {
+       $result = $IMPORT_TYPE{ $value };
+    } elsif ( 'FILE' eq $key ) {
+        if ( index( $value, "/" ) != 0 ) {
+            $result = File::Spec->rel2abs( $value ) ;
+        }
+        unless ( -e $value ) {
+            lprint( 0, 1, "File $value does not exist\n" );
+            print "File $value does not exist\n";
+            exit(2);
+        }
+    }
+
+    return $result;
+
+}
+
+###########################################################################
+sub build_volume_list {
+    my $key = shift;
+    my $list_str = shift;
+
+    my @volumes = ();
+
+    if ( $key eq 'FILE' ) {
+        open( DATA, $list_str ) or die "Couldn't open file $list_str, $!";
+        my @volumes_f = <DATA>;
+        close( DATA );
+        chomp ( @volumes_f );
+        foreach my $volume_f ( @volumes_f ) {
+            if ( my @matches = $volume_f =~qr/^(\w{6})(00)?$/ ) {
+                push @volumes,  $matches[0];      
+            }
+        }
+    } elsif ( $key eq 'VOL' )  {
+        $list_str =~ s/\s/,/g;
+        my @volumes_c = split( ',', $list_str );
+        foreach my $volume_c ( @volumes_c ) {
+            if ( index( $volume_c, '-' ) != -1 ) {
+                my @pair = split( '-', $volume_c );
+                if ( @pair == 2 ) {
+                    my $first = substr( $pair[0], 0, 2 );
+                    my $volume_start = substr(  $pair[0], 2, 4 );
+                    my $volume_end = substr(  $pair[1], 2, 4 ); ; #$pair[1];
+                    if ( $volume_start =~qr/^\d{4}$/ && $volume_end =~qr/^\d{4}$/ ) {
+                        for ( my $volume = $volume_start; $volume <= $volume_end; $volume++ ) {
+                            my $v = sprintf( "%s%04d", $first, $volume );
+                            push @volumes, $v;         
+                        }
+                    }
+                }   
+            } elsif ( my @matches = $volume_c =~qr/^(\w{6})(00)?$/ ) {
+                push @volumes, $matches[0];  
+            }      
+        }
+    }
+
+    return @volumes;
+}
+
+###########################################################################
+# ATTENTION:
+# Please pay attention that device id is extracted correctly
+sub get_dev_id_from_volume {
+    my $volume = shift;
+
+    $volume =~ s/\s//g;
+    if ( $volume =~ qr/^\w{6}00$/ ) {
+        chop ( $volume );
+        chop ( $volume );
+    }
+
+    my @hac_output = get_csdisk_info( $volume );
+
+    my $dev_id = undef;
+    my $volume00 = $volume . "00";
+    foreach my $line ( @hac_output ) {
+        $line = trim( $line );
+        my @line_array = split( '  ', $line );
+        # remove strings containing only white spaces
+        @line_array = grep /\S/, @line_array;
+        if ( ( @line_array == 6 ) && ( $line_array[0] eq $volume00 ) ) {
+            $dev_id = trim( $line_array[2] );
+        }
+    }
+
+    if ( !defined $dev_id ) {
+        lprint( "Could not extract device id for disk $volume\n" );
+        print "Could not extract device id for disk $volume\n";
+        exit(1);
+    }
+    
+    return $dev_id;
+}
+
+###########################################################################
+# ATTENTION:
+# You need to rewrite this subroutine
+# to get mover name from device info output
+sub extract_mover {
+    my $dev_id = shift;
+    my @hac_output = @_;
+
+    my $mover = read_value( 'Mover  ', $dev_id, @hac_output);
+    $mover =~ s/Mover \(//g;
+    $mover =~ s/\)//g;
+    unless ( $mover =~ qr/^mover\d{1,2}-disk$/ ) {
+        lprint( "Could not get mover for disk device id $dev_id\n" );
+        print "Could not get mover for disk device id $dev_id\n";
+        exit(3);
+    }
+
+    return $mover;
+}
+
+###########################################################################
+# ATTENTION:
+# Please pay attention that capacity is extracted correctly
+sub extract_capacity {
+    my $dev_id = shift;
+    my @hac_output = @_;
+
+    my $capacity = read_value( 'Capacity', $dev_id, @hac_output );
+    unless ( $capacity =~ qr/^\d*KB|\d*MB|\d*GB|\d*TB|\d*PB$/ ) {
+        lprint( "Could not get capacity for disk device id $dev_id\n" );
+        print "Could not get capacity for disk device id $dev_id\n";
+        exit(3);
+    }
+
+    return $capacity;
+}
+
+###############################################################################
+sub read_value {
+    my $type = shift;
+    my $dev_id = shift;
+    my @lines = @_;
+
+    my $value = 0;
+    foreach my $line ( @lines ) {
+        $line = trim( $line );
+        if ( index( trim( $line ), $type ) != -1 ) {
+            my @pair = split( '  ', $line );
+            # remove strings containing only white spaces
+            @pair = grep /\S/, @pair; 
+            $value = trim( $pair[1] ); 
+        }
+    }        
+    
+    return $value;
+}
+
+###############################################################################
+sub get_dev_info {
+    my $dev_id = shift;
+
+    my @hac_output = ();
+
+    my $hpssadm_command = "device info -device -id $dev_id";
+    @hac_output = process_valid_request( $hpssadm_command ) unless ( $TESTRUN );
+
+    @hac_output = split( "\n", "Mover Device Information
+Device Metadata  
+  Basic Information  
+    Device Name           1043b132-84ca-11e6-ae89-e61f13a75abf  
+    Device ID                                              888  
+    Device Type           Generic - SAN Attached Disk           
+    Media Block Size                                      4096  
+    Capacity                                      7811956728KB  
+    Starting Offset                                        4KB  
+    Mover                 Mover (mover5-disk)                   
+    Administrative State  Unlocked                              
+    Operational State     Enabled                               
+    Usage State           Idle                                  
+  Device Flags       
+    Read Enabled  on  Write Enabled  on  SAN3P-transfer Enabled  on  Multiple Mover Tasks  on  
+Device Record    
+  State             
+  Set State         
+  Volume Flags                       0  
+  Number Of Errors                   0  
+  Block Size                      4096  
+  TaosFD                             0  
+  Volume ID         D0088800            
+  LBPFlags                           0  
+  Bytes Read        20,103,536,560,305  
+  Bytes Written      9,672,578,383,872  
+
+" ) if ( $TESTRUN );
+    
+    lprint ( join( "\n", @hac_output ) );
+
+    return @hac_output;
+}
+
+###############################################################################
+sub get_csdisk_info {
+    my $volume = shift;
+
+    my @hac_output = ();
+
+    my $hpssadm_command = "volume info -type \"Core Server Disk Volume\" -name $volume";
+    @hac_output = process_valid_request( $hpssadm_command ) unless ( $TESTRUN );
+
+    my $volume00 = $volume . "00";
+    my $dev_id = substr( $volume, 3, 3);
+
+    @hac_output = split( "\n", " volume info -type \"Core Server Disk Volume\" -name $volume
+Core Server Disk Volume
+Name                     $volume00
+VV Condition             RWC       Retired  no
+Changes Pending          None      Offline  no
+More commonly used data  
+  Number of Extents                        35,113
+  Storage Class      6030 | GHI8 Small Files
+  Storage Map Flags
+  Usable Length                 3,999,717,654,528
+  Free Space                    1,825,585,496,064
+  Stripe Width                                  1
+  Time Last Read     Mar 24, 2022 12:51:23.000 PM
+  Time Last Written  Mar 24, 2022 12:11:18.000 PM
+  PVL Job ID                                   10
+Less commonly used data
+  Actual Length             3,999,721,844,736
+  PV Size                   3,999,721,844,736
+  Cluster Length                    2,097,152
+  VV BlockSize                        1048576
+  PV BlockSize                           4096
+  Creation Time   May 15, 2020 5:13:27.000 PM
+  VV ID
+    Object Id  00013c2c-03-00000001-01ea96bea10b5ec2-2eff
+Physical Volumes
+  Vol Name  Type                         Dev ID  Mvr IP Addr  Mvr                  Mvr Host
+  $volume00  Generic - SAN Attached Disk     $dev_id  3[55021]     Mover (mover6-disk)  hpssm6-adm.rzg.mpg.de
+
+" ) if ( $TESTRUN );
+
+    lprint ( join( "\n", @hac_output ) );
+
+    return @hac_output;
+}
+
+###########################################################################
+sub reset_bytes {
+    my $dev_id = shift;
+
+    reset_dev_info( $dev_id, "Bytes Read" );
+    reset_dev_info( $dev_id, "Bytes Written" );
+    reset_dev_info( $dev_id, "Number Of Errors" );
+}
+
+###########################################################################
+sub reset_dev_info {
+    my $dev_id = shift;
+    my $info = shift;
+
+    my $out = '';
+    $out =
+'  Bytes Read           0  
+  Bytes Written        0  
+Operation succeeded.
+' if ( $TESTRUN );
+
+    my $hpssadm_command = "device update -type \"Mover Device Information\" -id $dev_id -field \"Device Record\" -subfield \"$info\"";
+    print "Going to send hpssadm_command = $hpssadm_command\n" if ( $TESTRUN );
+    $out = join( "\n", process_valid_request( $hpssadm_command ) ) unless ( $TESTRUN );
+    if ( index( $out, 'Operation succeeded.' ) == -1 ) {
+        print "Could not reset $info for device $dev_id\n$out";
+        lprint( "Could not reset $info for device $dev_id\n$out" );
+        exit(3);
+    }
+}
+
+###########################################################################
+sub print_n_tasks {
+    my $n = shift;
+
+    my @hac_output = ();
+    my $hpssadm_command = "task list";
+    @hac_output = process_valid_request( $hpssadm_command );
+    print @hac_output[-($n+1)..-1];
+}
+
+###########################################################################
+sub process_valid_request {
+    my $request = shift;
+
+    lprint( "Going to send hpssadm_command = $request\n" );
+
+    my $host     = $CMDSRV;
+    my $port     = $TCPPORT;
+    my $clsocket =
+        IO::Socket::INET->new( PeerAddr => $host,
+                               PeerPort => $port,
+                               Proto    => 'tcp' );
+    my @answer = ();
+    if ( $clsocket ) {
+        #my $request = "device list -sick";
+        print $clsocket $request;
+        print $clsocket "\n";
+      
+        while ( my $line = <$clsocket> ) {
+            if ( $line =~ /^_END_/ ) {
+                close( $clsocket );
+                return @answer;
+            }
+            lprint( $line );
+            push @answer, $line;
+        }
+    }
+    else {
+        lprint( "has is not running. Restarting it.\n" );
+        print STDERR "'has' is not running. Going to restart it. Please wait...\n\n";
+
+        # ATTENTION:
+        # we use systemctl to restart has service.
+        # this service should be configured on the machine
+        # if not, change the following line
+        my $status = system( "systemctl start has" );
+
+        if ( $status != 0 ) {
+            print "Failed to restart 'has'. Please contact hac administrator.\n"; 
+            exit( $status ) 
+        }
+
+        sleep( 12 ) unless ( $TESTRUN );
+
+        my $answer = "";
+        $answer = "Restarted 'has'. Please try your command again.\n" if ( has_running() );
+        $answer = "Failed to restart 'has'. Please contact hac administrator.\n" unless ( has_running() );
+        
+        lprint( $answer );
+        print STDERR $answer;
+    }
+}
+
+#################################################################
+sub print_usage {
+    my $word = shift;
+
+    if ( $word eq 'list mount tapes' ) {
+        list_mount_tapes( '-h' );
+        return;
+    }
+
+    foreach my $k ( sort keys %dict_help ) {
+        if ( !( defined $word ) || ( index( $k, $word ) != -1 ) ) {
+            my $line = "hac $k : " . $dict_help { $k } . "\n";
+            my $terminal_width = `tput cols`;
+            $Text::Wrap::columns=$terminal_width-10;
+            $Text::Wrap::separator= "\n\t";
+            print wrap("","",$line);
+        }
+    }
+}
+
+################################################################################
+sub has_running {
+    my $has_sid = `ps -o sid= -C has`;
+    
+    if ( ( defined $has_sid ) && !( $has_sid eq '' ) ) {
+        my $ps_out = `ps -o cmd= -g \$(ps -o sid= -p $has_sid)`;
+        return ( index( $ps_out, 'hpssadm' ) != -1 );
+    }
+    
+    return 0;
+}
+
+#################################################################
+sub print_help {
+    print "\"has\" and \"hac\" is a client-server application. 
+\"has\" is a server part. 
+    It takes care of starting and running hpss adm and waits for client commands. 
+\"hac\" is a client part. 
+    It translates simple \"hac\" commands to complicated hpssadm commands 
+    and sends them to \"has\".\n
+Usage:\n";
+    print_usage();
+}
+
+##############################################################################
+# Start logging
+sub log_start {
+    lprint( "--------------------------------------------------\n" );
+}
+
+###############################################################################
+sub lprint {
+    my ( $string ) = @_;
+
+    if ( ( defined $string)  && !( $string=~/^\s*\n*$/ ) ) {
+        open( LOG, ">>$LOGF" ) || warn "Cannot write to '$LOGF': $!";
+        print LOG POSIX::strftime( '%Y-%m-%d %H:%M:%S', localtime ) . "- $string";
+        close( LOG );
+    }
+}
+
+###############################################################################
+sub trim { 
+    my $s = shift; 
+
+    if ( defined $s) {
+        $s =~ s/^\s+|\s+$//g;
+    }
+    
+     return $s 
+};
+
diff --git a/hac.dict b/hac.dict
new file mode 100644
index 0000000000000000000000000000000000000000..7e4081fbbce843ab968e2e5cccfe56ac0abd0e3c
--- /dev/null
+++ b/hac.dict
@@ -0,0 +1,253 @@
+#hac config
+# ATTENTION:
+# Please adapt these values
+storage_class_id_regex:\d{2,4}
+device_id_regex:\d{2,3}
+pvl_job_regex:\d{8}
+disk_volume_regex:\w{6}(00)?
+tape_volume_regex:\w{6}(00)?
+pvrs_regex:pvr\slib1|pvr\slib2
+media_types_regex:mt\slto5|mt\slto6|mt\slto7|mt\slto7m8|mt\slto8
+
+#hac command : command pattern : hpssadm command pattern : description
+
+# configuration info commands
+conf info tsc TAPE_STORAGE_CLASS_ID:conf info (tsc storage_class_id_regex(,storage_class_id_regex)*):configuration info -type "Tape Storage Class Configuration" -id [TSC]:Displays the specified tape storage class configuration. Multiple IDs may be specified,for example, 120,122
+
+## decices and drives commands
+# device list commands
+list devall:list devall:device list -all:list all devices and drives
+list devsick:list devsick:device list -sick:list sick devices and drives
+list dev DEVICE_ID:list (dev device_id_regex((,device_id_regex)*(-device_id_regex)*)*):device list -id [DEV]:list device for the specified device ID. Multiple IDs may be specified,for example, 30-34,37,60-62
+
+# device info commands
+info drive DRIVE_ID:info (drive device_id_regex((,device_id_regex)*(-device_id_regex)*)*):device info -drive -id [DRIVE]:list drive info for the specified device ID. Multiple IDs may be specified,for example, 30-34,37,60-62
+info dev DEVICE_ID:info (dev device_id_regex((,device_id_regex)*(-device_id_regex)*)*):device info -device -id [DEV]:list device info for the specified device ID. Multiple IDs may be specified,for example, 30-34,37,60-62
+
+# device update commands
+update dev DEVICE_ID readon:update (dev device_id_regex) readon:device update -type "Mover Device Information" -id [DEV] -field "Device Flags" -subfield "Read Enabled" on:set read enabled flag on for the specified device ID
+
+update dev DEVICE_ID readoff:update (dev device_id_regex) readoff:device update -type "Mover Device Information" -id [DEV] -field "Device Flags" -subfield "Read Enabled" off:set read enabled flag off for the specified device ID
+
+update dev DEVICE_ID writeon:update (dev device_id_regex) writeon:device update -type "Mover Device Information" -id [DEV] -field "Device Flags" -subfield "Write Enabled" on:set write enabled flag on for the specified device ID
+
+update dev DEVICE_ID writeoff:update (dev device_id_regex) writeoff:device update -type "Mover Device Information" -id [DEV] -field "Device Flags" -subfield "Write Enabled" off:set write enabled flag off for the specified device ID
+
+reset dev DEVICE_ID err:reset (dev device_id_regex) err:device update -type "Mover Device Information" -id [DEV] -field "Device Record" -subfield "Number Of Errors":reset number of errors for the specified device ID
+
+reset dev DEVICE_ID br:reset (dev device_id_regex) br:device update -type "Mover Device Information" -id [DEV] -field "Device Record" -subfield "Bytes Read":reset bytes read for the specified device ID
+
+reset dev DEVICE_ID bw:reset (dev device_id_regex) bw:device update -type "Mover Device Information" -id [DEV] -field "Device Record" -subfield "Bytes Written":reset bytes written for the specified device ID
+
+# drive update commands
+reset drive DRIVE_ID mnt:reset (drive device_id_regex) mnt:device update -type "PVL Drive Information" -id [DRIVE] -field "Mounts Since Last Maintenance" 0:reset mounts since last maintenance for the specified drive ID
+
+# device lock commands
+lock dev DEVICE_ID:lock (dev device_id_regex((,device_id_regex)*(-device_id_regex)*)*):device lock -device -id [DEV]:lock device. Multiple IDs may be specified,for example, 30-34,37,60-62
+unlock dev DEVICE_ID:unlock (dev device_id_regex((,device_id_regex)*(-device_id_regex)*)*):device unlock -device -id [DEV]:unlock device. Multiple IDs may be specified,for example, 30-34,37,60-62
+
+# drive lock commands
+lock drive DRIVE_ID:lock (drive device_id_regex((,device_id_regex)*(-device_id_regex)*)*):device lock -drive -id [DRIVE]:lock drive specified with ID. Multiple IDs may be provided,for example, 30-34,37,60-62
+unlock drive DRIVE_ID:unlock (drive device_id_regex((,device_id_regex)*(-device_id_regex)*)*):device unlock -drive -id [DRIVE]:unlock drive specified with ID. Multiple IDs may be specified,for example, 30-34,37,60-62
+lock drive DISK_VOLUME:lock (drive \w{6}(((,|\s)\w{6})*(-\w{6})*)*):device lock -drive -id [DRIVE]:lock disk drive specified with its volume name (6 characters). Multiple names may be specified,for example, D00300-D00304,D00371,D00600-D00602
+unlock drive DISK_VOLUME:unlock (drive \w{6}(((,|\s)\w{6})*(-\w{6})*)*):device unlock -drive -id [DRIVE]:unlock disk drive specified with its volume name (6 characters). Multiple names may be specified,for example, D00300-D00304,D00371,D00600-D00602
+
+# device repair command
+rep dev DEVICE_ID:rep (dev device_id_regex((,device_id_regex)*(-device_id_regex)*)*):device repair -device -id [DEV]:mark the specified device as repaired. Multiple IDs may be specified,for example, 30-34,37,60-62
+
+rep reset dev DEVICE_ID:rep reset (dev device_id_regex):device repair -device -id [DEV]:reset bytes read, bytes written, number of errors for the specified device and mark this device as repaired. Only one device may be specified.
+
+# drive repair command
+rep drive DRIVE_ID:rep (drive device_id_regex((,device_id_regex)*(-device_id_regex)*)*):device repair -drive -id [DRIVE]:mark the specified drive as repaired. Multiple IDs may be specified,for example, 30-34,37,60-62
+
+# drive dismount command
+dism drive DRIVE_ID:dism (drive device_id_regex((,device_id_regex)*(-device_id_regex)*)*):device dismount -id [DRIVE]:attempt to dismount the specified drive. Multiple IDs may be specified,for example, 30-34,37,60-62
+
+## health status command
+hs:hs:health_status info:display the overall server, device, and space threshold status
+
+## help commands
+helpha:helpha:help:print hpssadm help
+
+## list commands
+# list type commands
+list alev:list alev:list -type "Alarms and Events":list alarms and events
+list cos:list cos:list -type "Classes of Service":list classes of Service
+list devdr:list devdr:list -type "Devices and Drives":list devices and drives
+list filefam:list filefam:list -type "File Families": list file families
+list fsjl:list fsjl:list -type "Filesets & Junctions List":list filesets & junctions
+list hi:list hi:list -type "Hierarchies":list hierarchies
+list logpol:list logpol:list -type "Logging Policies":list logging policies
+list migpol:list migpol:list -type "Migration Policies":list migration policies
+list confsc:list confsc:list -type "Configured Storage Classes":list configured storage classes
+list activesc:list activesc:list -type "Active Storage Classes":list active storage classes
+list purpol:list purpol:list -type "Purge Policies":list purge policies
+list restus:list restus:list -type "Restricted Users":list restricted users
+list rtmsum:list rtmsum:list -type "RTM Summary List":list rtm summary list
+list srv:list srv:list -type "Servers":list servers
+list subsys:list subsys:list -type "Subsystems":list Subsystems
+list tmreq:list tmreq:list -type "Tape Mount Requests":list tape mount requests
+list tcreq:list tcreq:list -type "Tape Check-In Requests":list tape check-in requests
+
+## pvl job commands
+# pvl job list commands
+list pvlall:list pvlall:pvl_job list:list pvl jobs
+list pvlniu:list pvlniu:pvl_job list -not_in_use:list pvl jobs not in use
+list pvlid PVL_JOB_ID:list (pvlid pvl_job_regex((,pvl_job_regex)*(-pvl_job_regex)*)*):pvl_job list -id [PVLID]:list the specified pvl job. Multiple IDs may be specified,for example, 11111111-11111113,11111125,11111130-11111132
+
+# pvl job info command
+info pvlid PVL_JOB_ID:info (pvlid pvl_job_regex((,pvl_job_regex)*(-pvl_job_regex)*)*):pvl_job info -id [PVLID]:list the specified pvl job info. Multiple IDs may be specified,for example, 11111111-11111113,11111125,11111130-11111132
+
+# pvl job cancel command
+cancel pvlid PVL_JOB_ID:cancel (pvlid pvl_job_regex((,pvl_job_regex)*(-pvl_job_regex)*)*):pvl_job cancel -id [PVLID]:cancel the specified pvl job. Multiple IDs may be specified,for example, 11111111-11111113,11111125,11111130-11111132
+
+## rtm commands
+list rtmall:list rtmall:rtm list:list rtm summary
+
+# volume create disk commands
+create disk scid STORAGE_CLASS_ID vol VOLUME (VOLUME...):create disk (scid storage_class_id_regex) (vol disk_volume_regex(((,|\s)disk_volume_regex)*(-disk_volume_regex)*)*):volume create_disk -field "Initialization Fields" -subfield "Storage Class" [SCID] -field "Optional" -subfield "PV Size" [SIZE] -field "Use starting label" -subfield "Volume Label" [VOL]:Create core server disk resource(s) with a specified volume label for a storage class specified with an ID (hac create disk scid 222 ...), use 6 (D00111) or 8-character (D0011100) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "D00111 D00115" or "D00111,D00115,D00200-D00202"
+
+create disk scname STORAGE_CLASS_NAME vol VOLUME (VOLUME...):create disk (scname .*) (vol disk_volume_regex(((,|\s)disk_volume_regex)*(-disk_volume_regex)*)*):volume create_disk -field "Initialization Fields" -subfield "Storage Class" "[SCNAME]" -field "Optional" -subfield "PV Size" [SIZE] -field "Use starting label" -subfield "Volume Label" [VOL]:Creates core server disk resource with a specified volume label for a storage class specified with a full name (hac create disk scname '222 | AFS Large Files - 2nd copy' ...), use 6 (D00111) or 8-character (D0011100) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "D00111 D00115" or "D00111,D00115,D00200-D00202"
+
+create disk down scid STORAGE_CLASS_ID vol VOLUME (VOLUME...):create disk down (scid storage_class_id_regex) (vol disk_volume_regex(((,|\s)disk_volume_regex)*(-disk_volume_regex)*)*):volume create_disk -field "Initialization Fields" -subfield "Storage Class" [SCID] -field "Optional" -subfield "PV Size" [SIZE] -field "Optional" -subfield "Create DOWN" "on" -field "Use starting label" -subfield "Volume Label" [VOL]:Creates core server disk resource (in a DOWN state) with a specified volume label for a storage class specified with an ID (hac create disk down scid 222 ...), use 6 (D00111) or 8-character (D0011100) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "D00111 D00115" or "D00111,D00115,D00200-D00202"D00202"
+
+create disk down scname STORAGE_CLASS_NAME vol VOLUME (VOLUME...):create disk down (scname .*) (vol disk_volume_regex(((,|\s)disk_volume_regex)*(-disk_volume_regex)*)*):volume create_disk -field "Initialization Fields" -subfield "Storage Class" "[SCNAME]" -field "Optional" -subfield "PV Size" [SIZE] -field "Optional" -subfield "Create DOWN" "on" -field "Use starting label" -subfield "Volume Label" [VOL]:Creates core server disk resource (in a DOWN state) with a specified volume label for a storage class specified with a full name (hac create disk down scname '222 | AFS Large Files - 2nd copy' ...), use 6 (D00111) or 8-character (D0011100) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "D00111 D00115" or "D00111,D00115,D00200-D00202"
+
+create disk scid STORAGE_CLASS_ID file FILE:create disk (scid storage_class_id_regex) (file .*):volume create_disk -field "Initialization Fields" -subfield "Storage Class" [SCID] -field "Optional" -subfield "PV Size" [SIZE] -field "Use list in external file" -subfield "File" "[FILE]":Creates core server disk resources from a list in a specified file for a storage class specified with an ID (hac create disk scid 222 ...)
+
+create disk scname STORAGE_CLASS_NAME file FILE:create disk (scname .*) (file .*):volume create_disk -field "Initialization Fields" -subfield "Storage Class" "[SCNAME]" -field "Optional" -subfield "PV Size" [SIZE] -field "Use list in external file" -subfield "File" "[FILE]":Creates core server disk resources from a list in a specified file for a storage class specified with a full name (hac create disk scname '222 | AFS Large Files - 2nd copy' ...)
+
+create disk down scid STORAGE_CLASS_ID file FILE:create disk down (scid storage_class_id_regex) (file .*):volume create_disk -field "Initialization Fields" -subfield "Storage Class" [SCID] -field "Optional" -subfield "PV Size" [SIZE] -field "Optional" -subfield "Create DOWN" "on" -field "Use list in external file" -subfield "File" "[FILE]":Creates core server disk resources (in a DOWN state) from a list in a specified file for a storage class specified with an ID (hac create disk scid 222 ...)
+
+create disk down scname STORAGE_CLASS_NAME file FILE:create disk down (scname .*) (file .*):volume create_disk -field "Initialization Fields" -subfield "Storage Class" "[SCNAME]" -field "Optional" -subfield "PV Size" [SIZE] -field "Optional" -subfield "Create DOWN" "on" -field "Use list in external file" -subfield "File" "[FILE]":Creates core server disk resources (in a DOWN state) from a list in a specified file for a storage class specified with a full name (hac create disk scname '222 | AFS Large Files - 2nd copy' ...)
+
+create disk scid STORAGE_CLASS_ID vol VOLUME count COUNT inc INCREMENT:create disk (scid storage_class_id_regex) (vol disk_volume_regex) (count \d{1,2}) (inc \d{1}):volume create_disk -field "Initialization Fields" -subfield "Storage Class" [SCID] -field "Optional" -subfield "PV Size" [SIZE] -field "Use starting label" -subfield "Fill Count" [COUNT] -field "Use starting label" -subfield "Fill Increment" [INC] -field "Use starting label" -subfield "Volume Label" [VOL]:Creates a number of core server disk resources starting with a specified volume label with increment for a storage class specified with an ID (hac create disk scid 222 ...), use 6 (D00111) or 8-character (D0011100) volume label, note that all disks should be of the same size.
+
+create disk scname STORAGE_CLASS_NAME vol VOLUME count COUNT inc INCREMENT:create disk (scname .*) (vol disk_volume_regex) (count \d{1,2}) (inc \d{1}):volume create_disk -field "Initialization Fields" -subfield "Storage Class" "[SCNAME]" -field "Optional" -subfield "PV Size" [SIZE] -field "Use starting label" -subfield "Fill Count" [COUNT] -field "Use starting label" -subfield "Fill Increment" [INC] -field "Use starting label" -subfield "Volume Label" [VOL]:Creates a number of core server disk resources starting with a specified volume label with increment for a storage class specified with a full name (hac create disk scname '222 | AFS Large Files - 2nd copy' ...), use 6 (D00111) or 8-character (D0011100) volume label, note that all disks should be of the same size.
+
+create disk down scid STORAGE_CLASS_ID vol VOLUME count COUNT inc INCREMENT:create disk down (scid storage_class_id_regex) (vol disk_volume_regex) (count \d{1,2}) (inc \d{1}):volume create_disk -field "Initialization Fields" -subfield "Storage Class" [SCID] -field "Optional" -subfield "PV Size" [SIZE] -field "Optional" -subfield "Create DOWN" "on" -field "Use starting label" -subfield "Fill Count" [COUNT] -field "Use starting label" -subfield "Fill Increment" [INC] -field "Use starting label" -subfield "Volume Label" [VOL]:Creates a number of core server disk resources (in a DOWN state) using starting volume label, count and increment for a storage class specified with an ID (hac create disk scid 222 ...), use 6 (D00111) or 8-character (D0011100) volume label, note that all disks should be of the same size.
+
+create disk down scname STORAGE_CLASS_NAME vol VOLUME count COUNT inc INCREMENT:create disk down (scname .*) (vol disk_volume_regex) (count \d{1,2}) (inc \d{1}):volume create_disk -field "Initialization Fields" -subfield "Storage Class" "[SCNAME]" -field "Optional" -subfield "PV Size" [SIZE] -field "Optional" -subfield "Create DOWN" "on" -field "Use starting label" -subfield "Fill Count" [COUNT] -field "Use starting label" -subfield "Fill Increment" [INC] -field "Use starting label" -subfield "Volume Label" [VOL]:Creates a number of core server disk resources (in a DOWN state) using starting volume label, count and increment for a storage class specified with a full name (hac create disk scname '222 | AFS Large Files - 2nd copy' ...), use 6 (D00111) or 8-character (D0011100) volume label, note that all disks should be of the same size.
+
+# volume create tape commands
+create tape scid STORAGE_CLASS_ID vol VOLUME (VOLUME...):create tape (scid storage_class_id_regex) (vol tape_volume_regex(((,|\s)tape_volume_regex)*(-tape_volume_regex)*)*):volume create_tape -field "Initialization Fields" -subfield "Storage Class" [SCID] -field "Use starting label" -subfield "Volume Label" [VOL]:Creates core server tape resource with a specified volume label for a storage class specified with an ID (hac create tape scid 222 ...), use 6 (7A2345) or 8-character (7A234500) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "7A2340 7A2345" or "7A2340,7A2342,7A2345-7A2350"
+
+create tape scname STORAGE_CLASS_NAME vol VOLUME (VOLUME...):create tape (scname .*) (vol tape_volume_regex(((,|\s)tape_volume_regex)*(-tape_volume_regex)*)*):volume create_tape -field "Initialization Fields" -subfield "Storage Class" "[SCNAME]" -field "Use starting label" -subfield "Volume Label" [VOL]:Creates core server tape resource with a specified volume label for a storage class specified with a full name (hac create tape scname '222 | AFS Large Files - 2nd copy' ...), use 6 (7A2345) or 8-character (7A234500) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "7A2340 7A2345" or "7A2340,7A2342,7A2345-7A2350"
+
+create tape scid STORAGE_CLASS_ID file FILE:create tape (scid storage_class_id_regex) (file .*):volume create_tape -field "Initialization Fields" -subfield "Storage Class" [SCID] -field "Use list in external file" -subfield "File" "[FILE]":Creates core server tape resources from a list in a specified file for a storage class specified with an ID (hac create tape scid 222 ...)
+
+create tape scname STORAGE_CLASS_NAME file FILE:create tape (scname .*) (file .*):volume create_tape -field "Initialization Fields" -subfield "Storage Class" "[SCNAME]" -field "Use list in external file" -subfield "File" "[FILE]":Creates core server tape resources from a list in a specified file for a storage class specified with a full name (hac create tape scname '222 | AFS Large Files - 2nd copy' ...)
+
+create tape scid STORAGE_CLASS_ID vol VOLUME count COUNT inc INCREMENT:create tape (scid storage_class_id_regex) (vol tape_volume_regex) (count \d{1,2}) (inc \d{1}):volume create_tape -field "Initialization Fields" -subfield "Storage Class" [SCID] -field "Use starting label" -subfield "Fill Count" [COUNT] -field "Use starting label" -subfield "Fill Increment" [INC] -field "Use starting label" -subfield "Volume Label" [VOL]:Creates a number of core server tape resources using starting volume label, count and increment for a storage class specified with an ID (hac create tape scid 222 ...), use 6 (1A2B3C) or 8-character (1A2B3C00) volume label
+
+create tape scname STORAGE_CLASS_NAME vol VOLUME count COUNT inc INCREMENT:create tape (scname .*) (vol tape_volume_regex) (count \d{1,2}) (inc \d{1}):volume create_tape -field "Initialization Fields" -subfield "Storage Class" "[SCNAME]" -field "Use starting label" -subfield "Fill Count" [COUNT] -field "Use starting label" -subfield "Fill Increment" [INC] -field "Use starting label" -subfield "Volume Label" [VOL]:Creates a number of core server tape resources using starting volume label, count and increment for a storage class specified with a full name (hac create tape scname '222 | AFS Large Files - 2nd copy' ...), use 6 (1A2B3C) or 8-character (1A2B3C00) volume label
+
+# volume import disk commands
+import def disk it IMPORTTYPE vol VOLUME (VOLUME...):import def disk (it\sdef|it\ssc|it\sow) (vol disk_volume_regex(((,|\s)disk_volume_regex)*(-disk_volume_regex)*)*):volume import_disk -field "Media Type" "Generic - Default Disk" -field "Import Type" "[IT]" -field "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Import Generic - Default Disk resource with specified volume label with either default or scratch or overwrite import type (def, sc, ow), use 6 (D00111) or 8-character (D0011100) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "D00111 D00115" or "D00111,D00115,D00200-D00202"
+
+import def disk it IMPORTTYPE file FILE:import def disk (it\sdef|it\ssc|it\sow) (file .*):volume import_disk -field "Media Type" "Generic - Default Disk" -field "Import Type" "[IT]" -field "Build List of Volumes" -subfield "Use list in external file" -subfield "File" "[FILE]":Import Generic - Default Disk resources from a list in a specified file with either default or scratch or overwrite import type (def, sc, ow)
+
+import def disk it IMPORTTYPE vol VOLUME count COUNT inc INCREMENT:import def disk (it\sdef|it\ssc|it\sow) (vol disk_volume_regex) (count \d{1,2}) (inc \d{1}):volume import_disk -field "Media Type" "Generic - Default Disk" -field "Import Type" "[IT]" -field "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Count" [COUNT] -field "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Increment" [INC] -field "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Import Generic - Default Disk resources specified with a starting volume label and increment with either default or scratch or overwrite import type (def, sc, ow), use 6 (D00111) or 8-character (D0011100) volume label
+
+import san disk it IMPORTTYPE vol VOLUME (VOLUME...):import san disk (it\sdef|it\ssc|it\sow) (vol disk_volume_regex(((,|\s)disk_volume_regex)*(-disk_volume_regex)*)*):volume import_disk -field "Media Type" "Generic - SAN Attached Disk" -field "Import Type" "[IT]" -field "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Import Generic - SAN Attached Disk resource with specified volume label with either default or scratch or overwrite import type (def, sc, ow), use 6 (D00111) or 8-character (D0011100) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "D00111 D00115" or "D00111,D00115,D00200-D00202"
+
+import san disk it IMPORTTYPE file FILE:import san disk (it\sdef|it\ssc|it\sow) (file .*):volume import_disk -field "Media Type" "Generic - SAN Attached Disk" -field "Import Type" "[IT]" -field "Build List of Volumes" -subfield "Use list in external file" -subfield "File" "[FILE]":Import Generic - SAN Attached Disk resources from a list in a specified file with either default or scratch or overwrite import type (def, sc, ow)
+
+import san disk it IMPORTTYPE vol VOLUME count COUNT inc INCREMENT:import san disk (it\sdef|it\ssc|it\sow) (vol disk_volume_regex) (count \d{1,2}) (inc \d{1}):volume import_disk -field "Media Type" "Generic - SAN Attached Disk" -field "Import Type" "[IT]" -field "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Count" [COUNT] -field "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Increment" [INC] -field "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Import Generic - SAN Attached Disk resources specified with a starting volume label and increment with either default or scratch or overwrite import type (def, sc, ow), use 6 (D00111) or 8-character (D0011100) volume label
+
+# volume import tape commands
+import tape pvr PVR ndr MAXNUMDRIVES mt MEDIATYPE it IMPORTTYPE vol VOLUME (VOLUME...):import tape (pvrs_regex) (ndr \d{1,3}) (media_types_regex) (it\sdef|it\ssc|it\sow) (vol tape_volume_regex(((,|\s)tape_volume_regex)*(-tape_volume_regex)*)*):volume import_tape -field "PVR" "[PVR]" -field "Max Number of Drives" [NDR] -field "Import Information" -subfield "Media Type" "[MT]" -field "Import Information" -subfield "Import Type" "[IT]" -field "Import Information" -subfield "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Import MEDIATYPE (lto5, lto6, lto7, lto7m8, lto8) tape resource with specified volume label for a specified PVR with max number of drives = MAXNUMDRIVES and either default or scratch or overwrite import type (def, sc, ow), use 6 (7A2345) or 8-character (7A234500) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "7A2340 7A2345" or "7A2340,7A2342,7A2345-7A2350"
+
+import tape pvr PVR ndr MAXNUMDRIVES mt MEDIATYPE it IMPORTTYPE file FILE:import tape (pvrs_regex) (ndr \d{1,3}) (media_types_regex) (it\sdef|it\ssc|it\sow) (file .*):volume import_tape -field "PVR" "[PVR]" -field "Max Number of Drives" [NDR] -field "Import Information" -subfield "Media Type" "[MT]" -field "Import Information" -subfield "Import Type" "[IT]" -field "Import Information" -subfield "Build List of Volumes" -subfield "Use list in external file" -subfield "File" "[FILE]":Imports MEDIATYPE (lto5, lto6, lto7, lto7m8, lto8) tape resources for a specified PVR from a list in a specified file with max number of drives = MAXNUMDRIVES and either default or scratch or overwrite import type (def, sc, ow), use 6 (7A2345) or 8-character (7A234500) volume label
+
+import tape pvr PVR ndr MAXNUMDRIVES mt MEDIATYPE it IMPORTTYPE vol VOLUME count COUNT inc INCREMENT:import tape (pvrs_regex) (ndr \d{1,3}) (media_types_regex) (it\sdef|it\ssc|it\sow) (vol tape_volume_regex) (count \d{1,2}) (inc \d{1}):volume import_tape -field "PVR" "[PVR]" -field "Max Number of Drives" [NDR] -field "Import Information" -subfield "Media Type" "[MT]" -field "Import Information" -subfield "Import Type" "[IT]" -field "Import Information" -subfield "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Count" [COUNT] -field "Import Information" -subfield "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Increment" [INC] -field "Import Information" -subfield "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Import MEDIATYPE (lto5, lto6, lto7, lto7m8, lto8) tape resources for a specified PVR specified with a starting volume label and increment with max number of drives = MAXNUMDRIVES and either default or scratch or overwrite import type (def, sc, ow), use 6 (1A2B3C) or 8-character (1A2B3C00) volume label
+
+# volume delete disk resources commands
+del disk vol VOLUME (VOLUME...):del disk (vol disk_volume_regex(((,|\s)disk_volume_regex)*(-disk_volume_regex)*)*):volume delete -field "Use starting label" -subfield "Volume Label" [VOL]:Delete specified disk volume, use 6 (D00111) or 8-character (D0011100) volume label, Bytes Read and Bytes Written values will be resetted before disk deletion, multiple volume labels separated by a space, a comma or a dash can be specified for example, "D00111 D00115" or "D00111,D00115,D00200-D00202"
+
+del disk file FILE:del disk (file .*):volume delete -field "Use list in external file" -subfield "File" "[FILE]":Delete disk volumes specified in a file, Bytes Read and Bytes Written values will be resetted before disk deletion
+
+# volume delete tape resources commands
+del tape vol VOLUME (VOLUME...):del tape (vol tape_volume_regex(((,|\s)tape_volume_regex)*(-tape_volume_regex)*)*):volume delete -field "Use starting label" -subfield "Volume Label" [VOL]:Delete specified tape volume, use 6 (7A2345) or 8-character (7A234500) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "7A2340 7A2345" or "7A2340,7A2342,7A2345-7A2350"
+
+del tape file FILE:del tape (file .*):volume delete -field "Use list in external file" -subfield "File" "[FILE]":Delete tape volumes specified in a file
+
+del tape vol VOLUME count COUNT inc INCREMENT:del tape (vol tape_volume_regex) (count \d{1,2}) (inc \d{1}):volume delete -field "Use starting label" -subfield "Fill Count" [COUNT] -field "Use starting label" -subfield "Fill Increment" [INC] -field "Use starting label" -subfield "Volume Label" [VOL]:Delete a number of tape volumes specified with a starting volume label and increment, use 6 (1A2B3C) or 8-character (1A2B3C00) volume label
+
+# volume export disk commands
+export disk vol VOLUME (VOLUME...):export disk (vol disk_volume_regex(((,|\s)disk_volume_regex)*(-disk_volume_regex)*)*):volume export -field "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Export specified disk volume, use 6 (D00111) or 8-character (D0011100) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "D00111 D00115" or "D00111,D00115,D00200-D00202"
+
+export disk file FILE:export disk (file .*):volume export -field "Build List of Volumes" -subfield "Use list in external file" -subfield "File" "[FILE]":Export disk volumes specified in a file
+
+export disk vol VOLUME count COUNT inc INCREMENT:export disk (vol disk_volume_regex) (count \d{1,2}) (inc \d{1}):volume export -field "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Count" [COUNT] -field "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Increment" [INC] -field "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Export a number of disk volumes specified with a starting volume label and increment, use 6 (D00111) or 8-character (D0011100) volume label
+
+# volume export tape commands
+export tape vol VOLUME (VOLUME...):export tape (vol tape_volume_regex(((,|\s)tape_volume_regex)*(-tape_volume_regex)*)*):volume export -field "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Export specified tape volume, use 6 (7A2345) or 8-character (7A234500) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "7A2340 7A2345" or "7A2340,7A2342,7A2345-7A2350"
+
+export tape eject vol VOLUME (VOLUME...):export tape eject (vol tape_volume_regex(((,|\s)tape_volume_regex)*(-tape_volume_regex)*)*):volume export -filed "Eject Tapes After Exporting" "yes" -field "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Export and eject specified tape volume, use 6 (7A2345) or 8-character (7A234500) volume label, multiple volume labels separated by a space, a comma or a dash can be specified for example, "7A2340 7A2345" or "7A2340,7A2342,7A2345-7A2350"
+
+export tape file FILE:export tape (file .*):volume export -field "Build List of Volumes" -subfield "Use list in external file" -subfield "File" "[FILE]":Export tape volumes specified in a file
+
+export tape eject file FILE:export tape eject (file .*):volume export -filed "Eject Tapes After Exporting" "yes" -field "Build List of Volumes" -subfield "Use list in external file" -subfield "File" "[FILE]":Export and eject tape volumes specified in a file
+
+export tape vol VOLUME count COUNT inc INCREMENT:export tape (vol tape_volume_regex) (count \d{1,2}) (inc \d{1}):volume export -field "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Count" [COUNT] -field "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Increment" [INC] -field "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Export a number of tape volumes specified with a starting volume label and increment, use 6 (1A2B3C) or 8-character (1A2B3C00) volume label
+
+export tape eject vol VOLUME count COUNT inc INCREMENT:export tape eject (vol tape_volume_regex) (count \d{1,2}) (inc \d{1}):volume export -filed "Eject Tapes After Exporting" "yes" -field "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Count" [COUNT] -field "Build List of Volumes" -subfield "Use starting label" -subfield "Fill Increment" [INC] -field "Build List of Volumes" -subfield "Use starting label" -subfield "Volume Label" [VOL]:Export a number of tape volumes specified with a starting volume label and increment, use 6 (1A2B3C) or 8-character (1A2B3C00) volume label
+
+# task list command
+task list:task list:task list:show status of submitted tasks
+
+# volume info commands
+info pvlvol PVL_VOLUME:info (pvlvol tape_volume_regex):volume info -type "PVL Volume Information" -name [PVLVOL]:list pvl volume information, use 6 or 8 digits volume, use 6 (7A2345) or 8-character (7A234500) volume label label
+info pvrvol PVR_VOLUME:info (pvrvol tape_volume_regex):volume info -type "PVR Cartridge Information" -name [PVRVOL]:list pvr cartridge information, use 6 (7A2345) or 8-character (7A234500) volume label, use 6 (7A2345) or 8-character (7A234500) volume label
+info csvol CS_VOLUME:info (csvol tape_volume_regex):volume info -type "Core Server Tape Volume" -name [CSVOL]:list core server tape volume, use 6 (7A2345) or 8-character (7A234500) volume label, use 6 (7A2345) or 8-character (7A234500) volume label
+info csdisk CS_DISK:info (csdisk tape_volume_regex):volume info -type "Core Server Disk Volume" -name [CSDISK]:list core server disk volume, use 6 (7A2345) or 8-character (7A234500) volume label, use 6 (7A2345) or 8-character (7A234500) volume label
+
+## volume update commands
+# pvr cartridge
+reset pvrvol PVR_VOLUME mnt:reset (pvrvol tape_volume_regex) mnt:volume update -type "PVR Cartridge Information" -name [PVRVOL] -field "Mounts Since Maintenance":reset mounts since maintenance for the specified pvr cartridge, use 6 (7A2345) or 8-character (7A234500) volume label
+
+# pvl volume
+update pvlvol PVL_VOLUME comment COMMENT:update (pvlvol tape_volume_regex) (comment .*):volume update -type "PVL Volume Information" -name [PVLVOL] -field "Comment" "[COMMENT]":set comment for the specified pvl volume, use 6 (7A2345) or 8-character (7A234500) volume label
+
+update pvlvol PVL_VOLUME rm comment:update (pvlvol tape_volume_regex) rm comment:volume update -type "PVL Volume Information" -name [PVLVOL] -field "Comment" "":remove comment for the specified pvl volume, use 6 (7A2345) or 8-character (7A234500) volume label
+
+# cs tape volume
+update csvol CS_VOLUME down:update (csvol tape_volume_regex) down:volume update -type "Core Server Tape Volume" -name [CSVOL] -field "VV Condition" DOWN:set the specified core server tape volume to down, use 6 (7A2345) or 8-character (7A234500) volume label
+
+update csvol CS_VOLUME rwc:update (csvol tape_volume_regex) rwc:volume update -type "Core Server Tape Volume" -name [CSVOL] -field "VV Condition" RWC:set the specified core server tape volume to rwc, use 6 (7A2345) or 8-character (7A234500) volume label
+
+update csvol CS_VOLUME ro:update (csvol tape_volume_regex) ro:volume update -type "Core Server Tape Volume" -name [CSVOL] -field "VV Condition" RO:set the specified core server tape volume to ro, use 6 (7A2345) or 8-character (7A234500) volume label
+
+update csvol CS_VOLUME eom:update (csvol tape_volume_regex) eom:volume update -type "Core Server Tape Volume" -name [CSVOL] -field "VV Condition" EOM:set the specified core server tape volume to eom, use 6 (7A2345) or 8-character (7A234500) volume label
+
+# cs disk volume
+update csdisk CS_DISK down:update (csdisk tape_volume_regex) down:volume update -type "Core Server Disk Volume" -name [CSDISK] -field "VV Condition" DOWN:set the specified core server disk volume to down, use 6 (7A2345) or 8-character (7A234500) volume label
+
+update csdisk CS_DISK rwc:update (csdisk tape_volume_regex) rwc:volume update -type "Core Server Disk Volume" -name [CSDISK] -field "VV Condition" RWC:set the specified core server disk volume to rwc, use 6 (7A2345) or 8-character (7A234500) volume label
+
+update csdisk CS_DISK ro:update (csdisk tape_volume_regex) ro:volume update -type "Core Server Disk Volume" -name [CSDISK] -field "VV Condition" RO:set the specified core server disk volume to ro, use 6 (7A2345) or 8-character (7A234500) volume label
+
+update csdisk CS_DISK eom:update (csdisk tape_volume_regex) eom:volume update -type "Core Server Disk Volume" -name [CSDISK] -field "VV Condition" EOM:set the specified core server disk volume to eom, use 6 (7A2345) or 8-character (7A234500) volume label
+
+# storage class list commands
+list scall:list scall:storage_class list -all:list all storage classes
+list scsick:list scsick:storage_class list -sick:list sick storage classes
+
+# storage class info command
+info scid STORAGE_CLASS_ID:info (scid storage_class_id_regex((,storage_class_id_regex)*(-storage_class_id_regex)*)*):storage_class info -id [SCID]:list the specified storage class info. Multiple IDs may be specified,for example, 11-12,15,20-22
+
+# storage class migration controls
+migr start scid STORAGE_CLASS_ID:migr start (scid storage_class_id_regex((,storage_class_id_regex)*(-storage_class_id_regex)*)*):storage_class migrate -id [SCID]:start migration for the storage class. Multiple IDs may be specified,for example, 11-12,15,20-22
+migr stop scid STORAGE_CLASS_ID:migr stop (scid storage_class_id_regex((,storage_class_id_regex)*(-storage_class_id_regex)*)*):storage_class migrate_stop -id [SCID]:stop migration for the storage class. Multiple IDs may be specified,for example, 11-12,15,20-22
+migr resume scid STORAGE_CLASS_ID:migr resume (scid storage_class_id_regex((,storage_class_id_regex)*(-storage_class_id_regex)*)*):storage_class migrate_resume -id [SCID]:resume migration for the storage class. Multiple IDs may be specified,for example, 11-12,15,20-22
+migr suspend scid STORAGE_CLASS_ID:migr suspend (scid storage_class_id_regex((,storage_class_id_regex)*(-storage_class_id_regex)*)*):storage_class migrate_suspend -id [SCID]:suspend migration for the storage class. Multiple IDs may be specified,for example, 11-12,15,20-22
+migr reread scid STORAGE_CLASS_ID:migr reread (scid storage_class_id_regex((,storage_class_id_regex)*(-storage_class_id_regex)*)*):storage_class migrate_reread_policy -id [SCID]:reread migration policy for the storage class. Multiple IDs may be specified,for example, 11-12,15,20-22
+
+# storage class purge controls
+purge start scid STORAGE_CLASS_ID:purge start (scid storage_class_id_regex((,storage_class_id_regex)*(-storage_class_id_regex)*)*):storage_class purge -id [SCID]:start purge for the storage class. Multiple IDs may be specified,for example, 11-12,15,20-22
+purge stop scid STORAGE_CLASS_ID:purge stop (scid storage_class_id_regex((,storage_class_id_regex)*(-storage_class_id_regex)*)*):storage_class purge_stop -id [SCID]:stop purge for the storage class. Multiple IDs may be specified,for example, 11-12,15,20-22
+purge resume scid STORAGE_CLASS_ID:purge resume (scid storage_class_id_regex((,storage_class_id_regex)*(-storage_class_id_regex)*)*):storage_class purge_resume -id [SCID]:resume purge for the storage class. Multiple IDs may be specified,for example, 11-12,15,20-22
+purge suspend scid STORAGE_CLASS_ID:purge suspend (scid storage_class_id_regex((,storage_class_id_regex)*(-storage_class_id_regex)*)*):storage_class purge_suspend -id [SCID]:suspend purge for the storage class. Multiple IDs may be specified,for example, 11-12,15,20-22
+purge reread scid STORAGE_CLASS_ID:purge reread (scid storage_class_id_regex((,storage_class_id_regex)*(-storage_class_id_regex)*)*):storage_class purge_reread_policy -id [SCID]:reread purge policy for the storage class. Multiple IDs may be specified,for example, 11-12,15,20-22
+
diff --git a/has b/has
new file mode 100755
index 0000000000000000000000000000000000000000..3c499ea668f2753d198b38a351ba7f8da8714ecf
--- /dev/null
+++ b/has
@@ -0,0 +1,320 @@
+#!/usr/bin/perl
+
+#
+# This file is part of has and hac - hpssadm wrapper
+#
+# Copyright (C) 2022 MPCDF
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+use strict;
+
+use IO::Socket;
+use IO::Handle;
+use POSIX;
+use POSIX ":sys_wait_h";
+use IPC::Open2;
+use FileHandle;
+
+use FindBin;
+use lib $FindBin::Bin;
+
+my $VERSION = 3.1;
+
+my $DEBUG = ( "-d" eq @ARGV[0] ) ? 1 : 0;
+
+my $HPSS_ADM_NOT_RUNNING = 222;
+
+# ATTENTION:
+# the log file has.log will be put in the directory where th executable 'hac' is
+# if needed, put different directory here
+my $LOGF = $FindBin::Bin . "/hac.log";
+
+# ATTENTION:
+# change port if neccessary, should be in sync with hac
+my $TCPPORT = 9090;
+
+my $ME = "has hpss_adm_server";
+
+STDOUT->autoflush( 1 );
+STDERR->autoflush( 1 );
+
+# ATTENTION:
+# put user and keytab path, change java location
+# or change the whole hpssadm call if neccessary
+my $HPSSADM = "/opt/hpss/bin/hpssadm.pl -b -U USER -a PATH_TO_KEYTAB -j /usr/bin 2>&1";
+( my $reader, my $writer ) = ( FileHandle->new, FileHandle->new );
+
+$SIG{ALRM} = \&reap_children;
+
+main_loop();
+
+exit;
+
+###########################################################################
+# Main application loop
+sub main_loop {
+    log_start();
+    my $listener = main_socket();
+    open_hpssadm();
+
+    while ( 1 ) {
+        my $new_sock = $listener->accept();
+        $new_sock->autoflush( 1 );
+        my $peerhost = $new_sock->peerhost;
+        lprint( "New connection from $peerhost\n" );
+        print_with_time( "New connection from $peerhost\n" ) if ( $DEBUG );
+        my $pid = fork;
+        if ( $pid ) {
+            # The parent process
+            my %peer;
+            $peer{$pid} = $peerhost;
+        } else {
+            # The child process
+            service_request( $new_sock );
+            exit;
+        }
+        waitpid $pid, 0; # 0 is blocking, hac waits till hpssadm is restarted. 
+                         # WNOHANG (use POSIX 'WNOHANG';) is non-blocking, hac exists immidiately
+        my $status = ($? >> 8);
+        if ($status == $HPSS_ADM_NOT_RUNNING) {
+            open_hpssadm();
+            my $answer = "";
+            $answer = "Restarted hpssadm. Please try your command again.\n" if ( hpssadm_running() );
+            $answer = "Failed to restart hpssadm. Please contact hac administrator.\n" unless ( hpssadm_running() );
+            lprint( $answer );
+            print $new_sock "$answer\n";
+            print $new_sock "_END_\n";
+            close( $new_sock ) && lprint( "Parent process $$ closed socket '$new_sock'\n" ) || lprint( "Parent process $$ failed to close socket '$new_sock': $!\n" ); 
+        }
+        reap_children();
+    }
+}
+
+###########################################################################
+# Open main listening socket
+sub main_socket {
+    # Create a socket to listen on.
+    my $listener = IO::Socket::INET->new( LocalHost => '127.0.0.1', LocalPort => $TCPPORT, Listen => 8, Reuse => 1 );
+
+    if ( ! $listener) {
+        lprint( "Create socket for listening failed: $!\n" );
+        print_with_time( "Create socket for listening failed: $!\n" )  if ( $DEBUG );
+        exit;
+    }
+
+    lprint( "Listening on port $TCPPORT\n" );
+    print_with_time( "Listening on port $TCPPORT\n" ) if ( $DEBUG );
+    $listener->listen;
+
+    return $listener;
+}
+
+###########################################################################
+sub service_request {
+    ( my $socket ) = @_;
+    my $request = read_client_command( $socket );
+    process_request( $socket, $request );
+
+    print $socket "_END_\n";
+    close( $socket ) && lprint( "Child process $$ closed socket '$socket'\n" ) || lprint( "Child process $$ failed to close socket '$socket': $!\n" );
+}
+
+###########################################################################
+sub read_client_command {
+    my $socket = shift;
+
+    my $tmpbuffer = "";
+    my $request = "";
+    my $readmore = 1;
+
+    while($readmore) {
+        if ( $tmpbuffer = <$socket> ) {
+            $request .= $tmpbuffer;
+            $readmore = 0 if ( $request =~ '\n' );
+        }
+        else {
+            $readmore = 0;
+            close( $socket );
+            lprint( "Remote peer closed connection\n" );
+            print_with_time( "Remote peer closed connection\n" ) if ( $DEBUG );
+            exit;
+        }
+    }
+
+    $request =~ s/\r\n$//;
+    chomp( $request );
+
+    lprint( "read_client_command: request='$request'\n" );
+    print_with_time( "read_client_command: request='$request'\n" ) if ( $DEBUG );
+
+    return $request;
+}
+
+################################################################################
+sub open_hpssadm {
+
+    unless ( hpssadm_running() ) {
+        open2( $reader, $writer, $HPSSADM );
+        print $writer "pref update -field \"Echo Commands\" on\n";
+        print $writer "pref update -field \"Exit On Error\" off\n";
+        print $writer "echo\n";
+        sleep 12;
+        lprint( "--------opened hpssadm\n" );
+        print_with_time( "-------- opened hpssadm\n" );
+    }
+}
+
+################################################################################
+sub hpssadm_running {
+    my $ps_out = `ps -o cmd= -g \$(ps -o sid= -p $$)`;
+    return ( index( $ps_out, 'hpssadm' ) != -1 );
+}
+
+################################################################################
+sub close_hpssadm {
+    close $writer;
+    close $reader;
+}
+
+###########################################################################
+sub process_request {
+    my $socket = shift;
+    my $request = shift;
+
+    my $reqcmd = "$request";
+
+    lprint( "Child $$ processes request '$reqcmd'\n" );
+    print_with_time( "Child $$ processes request '$reqcmd'\n" ) if ( $DEBUG );
+
+    unless ( hpssadm_running() ) {
+            lprint( "hpssadm is not running. Restarting it.\n" );
+            print_with_time( "hpssadm is not running. Restarting it.\n" ) if ( $DEBUG );
+            my $answer = "hpssadm is not running. Going to restart it. Please wait...\n";
+            print $socket "$answer\n";
+            exit( $HPSS_ADM_NOT_RUNNING );
+    }
+
+    lprint( "-------- Sending command = $reqcmd to hpssadm\n" );
+    print_with_time( "-------- Sending command = $reqcmd to hpssadm\n" ) if ( $DEBUG );
+    print $writer "$reqcmd\n";
+    print $writer "device list -id 0\n";
+
+    my $line_number = 0;
+    my $start_from_line = 0;
+    my $very_first_line = '"/usr/bin/java"' . "\n";
+    my @lines;     
+   
+    while ( 1 ) {
+        my $line = readline $reader;    
+        $line_number++;
+        lprint( $line );
+        print_with_time( $line ) if ( $DEBUG );
+        last if (" device list -id 0\n" eq $line );
+        $start_from_line = 89 if ( $very_first_line eq $line ); # ignore startup echo
+        push @lines, $line if ( $line_number > $start_from_line ); 
+    }    
+    lprint( "-------- Finished processing command = $reqcmd\n" );
+    print_with_time( "-------- Finished processing command = $reqcmd\n" ) if ( $DEBUG );
+    # trim first lines ("Time Updated by System Manager..." "Time Received by Client...", etc.)
+    my @alines; 
+    my $no_device_string_found = 1;
+    my $device_id_1_string_found = 1;
+    for my $aline ( @lines ) {
+        if ( $no_device_string_found && index( $aline, 'No device in the list meets the criteria.' ) != -1 ) {
+            $no_device_string_found = 0;
+            next;
+        }   
+        if ( $device_id_1_string_found && index( $aline, 'Invalid device id 0' ) != -1 ) {
+            $device_id_1_string_found = 0;
+            next;
+        }
+        push @alines, $aline unless ( trim( $aline ) eq '' ||
+                                      index( $aline, 'Input command:' ) != -1 ||
+                                      index( $aline, 'Time Updated by System Manager' ) != -1 ||
+                                      index( $aline, 'Time Received by Client' ) != -1 );
+    }
+    my $answer = join( '', @alines );
+    print $socket "$answer\n";
+
+    # restart reader to get rid of old output
+    close $reader;
+    open $reader;
+}
+
+###########################################################################
+sub reap_children {
+    while ( ( my $childpid = waitpid( -1, &WNOHANG ) ) > 0 ) {
+        lprint( "Reaping child '$childpid'\n" );
+        print_with_time( "Reaping child '$childpid'\n" ) if ( $DEBUG );
+    }
+}
+
+#################################################################
+# Start logging
+sub log_start {
+    lprint( "--------------------------------------------------\n" );
+    lprint( "Starting $ME at " . POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime) . "\n" );
+    print_with_time ( "Starting $ME at " . POSIX::strftime('%Y-%m-%d %H:%M:%S', localtime) . "\n" );
+}
+
+###########################################################################
+sub lprint {
+    my ( $string ) = @_;
+
+    if ( ( defined $string)  && !( $string=~/^\s*\n*$/ ) ) {
+        open( LOG, ">>$LOGF" ) || warn "Cannot write to '$LOGF': $!";
+        print LOG POSIX::strftime( '%Y-%m-%d %H:%M:%S', localtime ) . "- $string";
+        close( LOG );
+    }
+}
+
+###########################################################################
+sub lprintf {
+    my @args = @_;
+
+    my $string = sprintf( $args[0], @args[1..$#args] );
+    lprint( $string );
+}
+
+###############################################################################
+sub is_in {
+    my $string = shift;
+    my @values = @_;
+
+    my $matches = 0;
+    foreach my $value ( @values ) {
+        if ( $value eq $string) {
+            $matches = 1;        
+        }         
+    }
+
+    return $matches;
+}
+
+###############################################################################
+sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };
+
+###############################################################################
+
+sub print_with_time {
+    my $string = shift;
+
+    my ($s, $min, $h, $d, $m, $y) = (localtime)[0,1,2,3,4,5];
+    printf "[%d-%d-%dT%d:%d:%d] ", $y+1900, $m+1, $d, $h, $min, $s;
+    print $string;
+}
+###############################################################################
+