# # Copyright (c) 2014-2015 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary Volume ComponentState Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::CS::Volume =head1 DESCRIPTION C is a derived class of L. It represents the state of an ONTAP Volume. A related class is L, which represents access to an ONTAP Volume. =head1 ATTRIBUTES The individual pieces of data that are part of the state of the Volume element are the attributes of the volume ComponentState. =over =item C<< volume >> The name of the volume element whose state is being represented. =item C<< vserver >> =item C<< aggregate >> =item C<< size >> =item C<< "name_ordinal" >> =item C<< dsid >> =item C<< msid >> =item C<< state >> =item C<< type >> =item C<< "volume_style" >> =item C<< "volume_ownership" >> =item C<< policy >> =item C<< user >> =item C<< group >> =item C<< "security_style" >> =item C<< "unix_permissions" >> =item C<< "junction_path" >> =item C<< "junction_path_source" >> =item C<< "junction_active" >> =item C<< parent >> =item C<< vsroot >> =item C<< comment >> =item C<< available >> =item C<< total >> Maps to: 7M ZAPI : size-total =item C<< used >> Maps to: 7M ZAPI : size-used =item C<< "percent_used" >> =item C<< autosize >> =item C<< "max_autosize" >> =item C<< "autosize_increment" >> =item C<< "flexgroup_uuid" >> =item C<< "flexgroup_msid" >> =item C<< "flexgroup_index" >> =item C<< "is_flexgroup" >> =item C<< "aggr_list" >> =item C<< "status" >> =item C<< nodes >> =item C<< files >> =item C<< "files_used" >> =item C<< "maxdir_size" >> =item C<< "space_guarantee" >> =item C<< "space_guarantee_enabled" >> =item C<< "min_readahead" >> =item C<< "atime_update" >> =item C<< "snapdir_access" >> =item C<< "percent_snapshot_space" >> =item C<< "snapshot_space_used" >> =item C<< "snapshot_policy" >> =item C<< "create_time" >> =item C<< "language" >> =item C<< "stripe_count" >> =item C<< "stripe_width" >> =item C<< "striping_epoch" >> =item C<< "properly_striped" >> =item C<< concurrency >> =item C<< optimize >> =item C<< "clone_volume" >> =item C<< "antivirus_on_access_policy" >> =item C<< uuid >> =item C<< "stripe_format" >> =item C<< "index_dir_enabled" >> =item C<< "load_sharing_source" >> =item C<< "move_target" >> =item C<< "max_write_alloc_blocks" >> =item C<< "is_inconsistent" >> =item C<< "fractional_reserve" >> =item C<< "ignore_inconsistent" >> =item C<< "create_ucode" >> =item C<< "svo_allow_rman" >> =item C<< compression >> =item C<< nosnapdir >> =item C<< "snapshot_clone_dependency" >> =item C<< "svo_checksum" >> =item C<< "svo_reject_errors" >> =item C<< "svo_enable" >> =item C<< "nbu_archival_snap" >> =item C<< extent >> =item C<< "no_atime_update" >> =item C<< "read_realloc" >> Filled in for CMode CLI, CMode ZAPI, 7Mode CLI and 7Mode ZAPI. Valid values are "on/off/space-optimized". Maps to: CM CLI: read-realloc CM ZAPI: read-realloc $value Invokes "volume-get-iter" API for CMode ZAPI 7M ZAPI: read_realloc read_realloc space_optimized Invokes "volume-list-info" API for 7Mode ZAPI 7M CLI: read_realloc Invokes "vol status $vol_name -v" command for 7Mode CLI =item C<< "convert_ucode" >> =item C<< "try_first" >> =item C<< "fs_size_fixed" >> =item C<< "schedsnapname" >> =item C<< nosnap >> =item C<< "guarantee" >> =item C<< "snapmirrored" >> =item C<< "no_i2p" >> =item C<< "nvfail" >> =item C<< plexes >> (Array) =item C<< "plex_count" >> =item C<< chksumstyle >> =item C<< chksumstatus >> =item C<< chksumenabled >> =item C<< disklist >> (Array) =item C<< diskcount >> =item C<< "mirror_status" >> =item C<< "is_snaplock" >> =item C<< "block_type" >> =item C<< raidsize >> =item C<< "size_used" >> =item C<< inconsistent >> =item C<< raidstatus >> =item C<< physical_replica >> =item C<< foreground >> =item C<< flexcache_cache_policy >> =item C<< flexcahce_fill_policy >> =item C<< in_nvfailed_state >> =item C<< filesys_size_fixed >> =item C<< space_mgmt_try_first >> =item C<< is_quiesced_on_disk >> =item C<< is_quiesced_in_memory >> =item C<< transition_state >> =item C<< is_copied_for_transition >> =item C<< is_transitioned >> =item C<< is_sis_volume >> =item C<< is_sis_logging_enabled >> =item C<< sis_space_saved >> =item C<< sis_space_saved_percent >> =item C<< dedupe_space_saved_percent >> =item C<< dedupe_space_saved >> =item C<< compression_space_saved >> =item C<< compression_space_saved_percent >> =item C<< instance_uuid >> Filled in for CMode CLI, CMode ZAPI, 7Mode CLI and 7Mode ZAPI. CMode ZAPI: Accessible through the field "instance-uuid" in the output of ZAPI "volume-get-iter" 7Mode CLI: Accessible through the field "Volume Instance UUID" in the output of command "vol status -v" 7Mode ZAPI: Accessible through the field "instance-uuid" of the ZAPI "volume-list-info" =item C<< provenance_uuid >> Filled in for CMode CLI, CMode ZAPI, 7Mode CLI and 7Mode ZAPI. CMode ZAPI: Accessible through the field "provenance-uuid" in the output of ZAPI "volume-get-iter" 7Mode CLI: Accessible through the field "Volume Provenance UUID" in the output of command "vol status -v" 7Mode ZAPI: Accessible through the field "provenance-uuid" of the ZAPI "volume-list-info" =item C<< flexcache_origin_volume >> =item C<< inodefile_public_capacity >> =item C<< files_private_used >> =item C<< inodefile_private_capacity >> =item C<< node >> =item C<< clone_parent_name >> If this volume is a clone, then this field contains the name of the parent volume. This field is filled in for 7Mode. (For CMode it is applicable only for early RR.0 builds) =item C<< clone_parent_bsnap_name >> If this volume is a clone, then this field contains the name of the parent snapshot. This field is filled in for 7Mode. (For CMode it is applicable only for early RR.0 builds) =item C<< clone_child_count >> =item C<< clone_parent_bsnap_id >> =item C<< clone_parent_dsid >> =item C<< clone_parent_msid >> =item C<< clone_parent_uuid >> =item C<< inode_parent_path_trans_enabled >> =item C<< extent_enabled >> =item C<< overwrite_reserve_required >> =item C<< overwrite_reserve_used >> =item C<< overwrite_reserve_used_actual >> =item C<< auto_snapshots_enabled >> =item C<< snap_mirror_enabled >> =item C<< fsid >> =item C<< language_str >> =item C<< oem_character_set >> =item C<< nfs_character_set >> =item C<< svo_enabled >> =item C<< svo_checksum_enabled >> =item C<< svo_rman_allowed >> =item C<< disk_root >> =item C<< is_mroot >> =item C<< compression_enabled >> =item C<< root_dir_gen >> =item C<< sched_snap_name >> =item C<< volume_check_status >> =item C<< volume_check_estimated_time_left >> =item C<< snap_autodelete_enabled >> =item C<< snap_autodelete_commitment >> =item C<< snap_autodelete_defer_delete >> =item C<< snap_autodelete_delete_order >> =item C<< snap_autodelete_prefix >> =item C<< snap_autodelete_target_free_space >> =item C<< snap_autodelete_trigger >> =item C<< snap_autodelete_destroy_list >> =item C<< is_invalid >> =item C<< is_unrecoverable >> =item C<< containing_aggregate_uuid >> =item C<< vserver_uuid >> =item C<< redirect_snapshot_id >> =item C<< is_data_protection_mirror >> =item C<< is_load_sharing_mirror >> =item C<< is_move_mirror >> =item C<< is_replica_volume >> =item C<< mirror_transfer_in_progress >> =item C<< size_available_for_snapshots >> =item C<< snapshot_reserve_size >> =item C<< is_readonly >> =item C<< is_cluster_volume >> =item C<< upgrade_64bit_estimate_age >> Filled in for CMode CLI. =item C<< upgrade_64bit_check_last_errno >> Filled in for CMode CLI. =item C<< upgrade_64bit_available_space >> Filled in for CMode CLI. =item C<< size_used_by_snapshots >> Filled in for CMode CLI. =item C<< is_compressed_volume >> Filled in for CMode CLI. =item C<< upgrade_64bit_grow_vol_size_by >> Filled in for CMode CLI. =item C<< upgrade_64bit_used_space >> Filled in for CMode CLI. =item C<< flexcache_connection_status >> Filled in for CMode CLI. =item C<< files_maximum_possible >> Filled in for CMode CLI. =item C<< upgrade_64bit_start_last_errno >> Filled in for CMode CLI. =item C<< dedupe_space_shared >> Filled in for CMode CLI. =item C<< upgrade_64bit_percent_used_space >> Filled in for CMode CLI. =item C<< flexcache_fill_policy >> Filled in for CMode CLI. =item C<< constituent_role >> Filled in for CMode CLI. =item C<< used_afs >> Filled in for CMode CLI. =item C<< directory_bitwidth >> Filled in for CMode CLI. =item C<< junction_parent >> Filled in for CMode CLI. =item C<< ignore_repository_check >> Filled in for CMode CLI. =item C<< is_constituent >> Filled in for CMode CLI. =item C< is_read_only > Filled in for 7Mode CLI. Describes whether the volume is read-only (value of this field can be true/false) =item C< raid_type > Filled in for 7Mode CLI. The raid-type (raid0/raid4/raid_dp/raid_tec) =item C< mirror_type > Filled in for 7Mode CLI. =item C< clones_list > (Array) This is the list of clones of this particular volume. This is a 7Mode-only field. =item C<< flexcache_min_reserve >> Filled in for CMode CLI. =item C<< vm_align_suffix >> Filled in for CMode/7Mode CLI. =item C<< is_moving >> Filled in for CMode CLI. =item C<< vm_align_sector >> Filled in for CMode/7Mode CLI. =item C<< transition_behavior >> Filled in for CMode CLI. =item C<< hybrid_cache_write_caching_ineligibility_reason >> Filled in for CMode CLI. =item C<< hybrid_cache_eligibility >> Filled in for CMode CLI. =item C<< vbn_zero_space_saved >> Filled in for CMode CLI. =item C<< vbn_zero_space_saved_percent >> Filled in for CMode CLI. =item C<< force_nvfail_on_dr >> Filled in for CMode CLI. =item C<< autosize_grow_threshold_percent >> Filled in for CMode CLI. =item C<< autosize_mode >> Filled in for CMode CLI. =item C<< min_autosize >> Filled in for CMode CLI. =item C<< autosize_shrink_threshold_percent >> Filled in for CMode CLI. =item C<< object_write_sync_period >> Filled in for CMode CLI. =item C<< is_reserve_constituent >> Filled in for CMode CLI. =item C<< enable_object_store >> Filled in for CMode CLI. =item C<< ols_aggr_list >> Filled in for CMode CLI. (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $ols_aggr_list = $obj->ols_aggr_list(); # $ols_aggr_list contains a reference to the array of values my @ols_aggr_list = $obj->ols_aggr_list(); # @ols_aggr_list contains the array of values Filled in for CMode CLI. =item C<< ols_constituent_count >> Filled in for CMode CLI. =item C<< ols_constituent_size >> Filled in for CMode CLI. =item C<< qos_policy_group >> Filled in for CMode CLI. =item C<< is_managed_by_service >> Filled in for CMode CLI/ZAPI. Maps to: CM CLI: is-managed-by-service CM ZAPI: is-managed-by-service $value Invokes "volume-get-iter" API for CMode ZAPI =item C<< enable_snapdiff >> Filled in for CMode CLI/ZAPI. Maps to: CM CLI: enable-snapdiff CM ZAPI: enable-snapdiff $value Invokes "volume-get-iter" API for CMode ZAPI =item C<< storage_service >> Filled in for CMode CLI/ZAPI. Maps to: CM CLI: storage-service CM ZAPI: storage-service $value Invokes "volume-get-iter" API for CMode ZAPI =item C<< is_volume_in_cutover >> Is Volume Move in Cutover Phase, possible value(s) are true,false Filled in for CMode CLI. =item C<< flexvol_metadata_fp >> Filled in for CMode CLI. =item C<< user_data_percent >> Filled in for CMode CLI. =item C<< quota_metafiles >> Filled in for CMode CLI. =item C<< volume_guarantee_fp_percent >> Filled in for CMode CLI. =item C<< fc_delegs_enabled >> *FlexCache Delegations, possible value(s) are true,false Filled in for CMode CLI. =item C<< snapmirror_destination_fp_percent >> Filled in for CMode CLI. =item C<< quota_metafiles_percent >> Filled in for CMode CLI. =item C<< volume_data_fp_percent >> Filled in for CMode CLI. =item C<< snapshot_spill >> Filled in for CMode CLI. =item C<< dedupe_metafiles_tmp_fp_percent >> Filled in for CMode CLI. =item C<< inodes >> Filled in for CMode CLI. =item C<< dedupe_metafiles >> Filled in for CMode CLI. =item C<< user_data >> Filled in for CMode CLI. =item C<< dedupe_metafiles_percent >> Filled in for CMode CLI. =item C<< tape_backup_metafiles_fp >> Filled in for CMode CLI. =item C<< volume_data_fp >> Filled in for CMode CLI. =item C<< dedupe_metafiles_fp_percent >> Filled in for CMode CLI. =item C<< dedupe_metafiles_tmp_percent >> Filled in for CMode CLI. =item C<< autosize_reset >> Filled in for CMode CLI. =item C<< delayed_free_fp >> Filled in for CMode CLI. =item C<< total_fp >> Filled in for CMode CLI. =item C<< tape_backup_metafiles_fp_percent >> Filled in for CMode CLI. =item C<< total_fp_percent >> Filled in for CMode CLI. =item C<< snapshot_spill_percent >> Filled in for CMode CLI. =item C<< inodes_percent >> Filled in for CMode CLI. =item C<< volume_filesystem_metadata >> Filled in for CMode CLI. =item C<< aggregate_size >> Filled in for CMode CLI. =item C<< used_including_snap_reserve >> Filled in for CMode CLI. =item C<< delayed_free_fp_percent >> Filled in for CMode CLI. =item C<< snapmirror_destination_fp >> Filled in for CMode CLI. =item C<< flexvol_metadata_fp_percent >> Filled in for CMode CLI. =item C<< dedupe_metafiles_tmp >> Filled in for CMode CLI. =item C<< dedupe_metafiles_fp >> Filled in for CMode CLI. =item C<< dedupe_metafiles_tmp_fp >> Filled in for CMode CLI. =item C<< volume_filesystem_metadata_percent >> Filled in for CMode CLI. =item C<< used_including_snap_reserve_percent >> Filled in for CMode CLI. =item C<< volume_guarantee_fp >> Filled in for CMode CLI. =item C<< effective_guarantee >> =item C<< upgrade_64bit_capacity >> Filled in for CMode ZAPI. =item C<< physical_used >> Filled in for CMode CLI/ZAPI. =item C<< upgrade_64bit_grow_space >> Filled in for CMode ZAPI. =item C<< percentage_snapshot_reserve_used >> Filled in for CMode ZAPI. =item C<< stripe_algorithm >> Filled in for CMode ZAPI. =item C<< percentage_snapshot_reserved >> Filled in for CMode ZAPI. =item C<< become_node_root_after_reboot >> Filled in for CMode ZAPI. =item C<< external_cache >> External Cache Policy Name Filled in for CMode CLI. =item C<< use_new_algorithm >> *Use New Algorithm, possible value(s) are true,false Filled in for CMode CLI. =item C<< percent_reserve_constituent >> *Percent Space For Reserve Data Constituent Filled in for CMode CLI. =item C<< max_namespace_constituent_size >> *Maximum Size of Namespace Constituent, possible value(s) are [KB,MB,GB,TB,PB] Filled in for CMode CLI/ZAPI. Maps to: CM CLI: max-namespace-constituent-size CM ZAPI: max-namespace-constituent-size $value =item C<< data_aggr_list >> *List of Aggregates for Data Constituents (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $data_aggr_list = $obj->data_aggr_list(); # $data_aggr_list contains a reference to the array of values my @data_aggr_list = $obj->data_aggr_list(); # @data_aggr_list contains the array of values If this field needs to be passed to the filter hash, the value for this field should be passed in as an arrayref # filter => { data_aggr_list = [ value1, value2...] } Filled in for CMode CLI. =item C<< ns_mirror_aggr_list >> *List of Aggregates for Namespace Mirrors (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $ns_mirror_aggr_list = $obj->ns_mirror_aggr_list(); # $ns_mirror_aggr_list contains a reference to the array of values my @ns_mirror_aggr_list = $obj->ns_mirror_aggr_list(); # @ns_mirror_aggr_list contains the array of values If this field needs to be passed to the filter hash, the value for this field should be passed in as an arrayref # filter => { ns_mirror_aggr_list = [ value1, value2...] } Filled in for CMode CLI. =item C<< namespace_aggregate >> *Namespace Aggregate Filled in for CMode CLI. =item C<< max_data_constituent_size >> *Maximum Size of each Data Constituent, possible value(s) are [KB,MB,GB,TB,PB] Filled in for CMode CLI/ZAPI. Maps to: CM CLI: max-data-constituent-size CM ZAPI: max-data-constituent-size $value Invokes "volume-get-iter" API for CMode ZAPI =item C<< snapmirror_metadata_percent >> Filled in for CMode ZAPI. =item C<< snapmirror_metadata >> Filled in for CMode CLI. =item C<< space_full_threshold_percent >> Volume Full Threshold Percent Filled in for CMode CLI. =item C<< space_nearly_full_threshold_percent >> Volume Nearly Full Threshold Percent Filled in for CMode CLI. =item C<< autosize_increment_percent >> Filled in for CMode CLI. =item C<< snapshot_count >> Number of Snapshot Copies in the Volume, possible value(s) are 0..255 Filled in for CMode CLI/ZAPI. =item C<< filesystem_size >> Filesystem Size, possible value(s) are [KB,MB,GB,TB,PB] Filled in for CMode CLI. =item C<< aggregate_size_available >> Filled in for CMode CLI. =item C<< unreachable_attr_action >> Action When Attributes Are Not Reachable possible value(s) are, return-generated,wait Filled in for CMode CLI. =item C<< caching_policy >> Caching Policy Name Filled in for CMode CLI/ZAPI. =item C<< snapshot_policy_uuid >> Snapshot policy uuid Filled in for CMode CLI. =item C<< junctionFileHandle >> Junction File Handle Filled in for CMode CLI. =item C<< qos_policy_group_uuid >> QoS Policy Group uuid Filled in for CMode CLI. =item C<< config_replication_flexcache_origin_volume >> FlexCache Origin Volume Name for config-replication Filled in for CMode CLI. =item C<< efficiency_policy >> Efficiency Policy Filled in for CMode CLI/ZAPI. Maps to: CMode ZAPI: efficiency-policy $value =item C<< vbn_bad_present >> VBN_BAD may be present in the active filesystem possible value(s) are, true,false Filled in for CMode CLI. =item C<< efficiency_policy_uuid >> UUID of the Efficiency Policy Filled in for CMode CLI. =item C<< excluded_from_balancer >> Is Capacity Balancer Disabled possible value(s) are, true,false Filled in for CMode CLI. =item C<< clone_parent_bsnap_uuid >> Clone Parent Base Snapshot UUID Filled in for CMode CLI. =item C<< clone_parent_type >> Clone Parent Volume Type Filled in for CMode CLI. =item C<< is_vol_on_hybrid_aggr >> Is Volume on a hybrid aggregate possible value(s) are, true,false Filled in for CMode CLI. =item C<< is_balance_eligible >> Is Eligible for the Balancer possible value(s) are, true,false Filled in for CMode CLI. =item C<< raid_cv >> =item C<< thorough_scrub >> =item C<< no_delete_log >> =item C<< striping >> =item C<< actual_guarantee >> =item C<< ha_policy >> =item C<< dlog_hole_reserve >> =item C<< upgraded_replica >> =item C<< resyncsnaptime >> =item C<< raidtype >> =item C<< snaplock_default_period >> Default retention period for files Filled in for CMode CLI/ZAPI iter and 7Mode CLI. =item C<< snaplock_maximum_period >> Maximum retention period for files Filled in for CMode CLI/ZAPI iter and 7Mode CLI. =item C<< snaplock_minimum_period >> Minimum retention period for files Filled in for CMode CLI/ZAPI iter and 7Mode CLI. =item C<< is_snaplock >> If the volume is SnapLock possible value(s) are, true,false Filled in for CMode CLI/ZAPI iter and 7Mode CLI. =item C<< snaplock_autocommit_period >> Default retention value is 60s Filled in for CMode CLI/ZAPI iter and 7Mode CLI. =item C<< max_constituent_size >> It is the maximum constituent size of a flexgroup volume in KB|MB|GB. Minimum size is 20MB Filled in for CMode CLI/ZAPI iter. =item C<< "volume_style_extended" >> New field 'volume-style-extended' was added to volume show, for extended volume-style =item C<< "default_extent_size" >> New field 'default-extent-size' added for MetaWAFL volumes. =item C<< "encrypt" >> Boolean. To enable encryption on volume. =item C<< qos_adaptive_policy_group >> Filled in for CMode CLI/ZAPI. =item C<< is_flexgroup_qtree_enabled >> The field 'is-flexgroup-qtree-enabled' was added for the flexgroup qtree support. Filled in for CMode CLI/ZAPI. =back =cut package NACL::CS::Volume; use strict; use warnings; use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use Data::Dumper; use Params::Validate qw(validate validate_pos SCALAR HASHREF); use base 'NACL::CS::ComponentState::ONTAP'; use NACL::C::Volume; use NACL::APISet::Exceptions::InvalidParamValueException qw(:try); use NACL::APISet::Exceptions::ResponseException qw(:try); use NACL::APISet::Exceptions::InvalidNodescopeNodeException (); use NACL::Exceptions::InvalidChoice; use NACL::CS::ComponentState::ZapiSkip qw(make_zapi_skip); use NACL::CS::ComponentState::ZapiArray qw(make_zapi_array); use NACL::C::_Mixins::AggregateVolume qw(_translate_options_values _handle_snaplock_type); use NACL::C::_Mixins::Volume qw(:all); use NACL::CS::_Mixins::ComponentState qw( sort_by_newest sort_by_oldest _arrange_by_create_time ); use NACL::C::UnitNormalization; use NACL::Exceptions::InvalidChoice; use NACL::Global; # Note: There is an "autosize" attribute and an "autosize_reset" attribute. # MethodMaker automatically creates a "foo_reset" method for every attribute # "foo" defined (this method resets, or deletes the value of this attribute # from the object). This causes a conflict. # # If "autosize" is declared first, then an "autosize_reset" method gets # created which resets the value of "autosize". When the attribute # "autosize_reset" is defined, MethodMaker finds this method already present # and does not overwrite it. The effect this has is that there is now no # get/set method for the attribute "autosize_reset". # # However, swapping the order of the declarations (i.e. "autosize_reset" # first, then "autosize") works. This way, both attributes have get/set methods # defined. [*] # # For this reason, if there are any packages with "foo" and "foo_reset" # as attributes, ensure that "foo_reset" is listed before "foo" in the # MethodMaker declaration. # # [*] Caveat: The only thing that doesn't work: there is no method to directly # reset the value of the "autosize" attribute. This is in any case of less # importance than being able to get/set the value of the attributes. use Class::MethodMaker [ # C-mode only scalar => 'vserver', scalar => 'volume', scalar => 'aggregate', scalar => 'size', scalar => 'name_ordinal', scalar => 'dsid', scalar => 'msid', scalar => 'state', scalar => 'type', scalar => 'volume_style', scalar => 'volume_ownership', scalar => 'policy', scalar => 'user', scalar => 'group', scalar => 'security_style', scalar => 'unix_permissions', scalar => 'junction_path', scalar => 'junction_path_source', scalar => 'junction_active', scalar => 'parent', scalar => 'vsroot', scalar => 'comment', scalar => 'available', scalar => 'total', scalar => 'used', scalar => 'percent_used', scalar => 'autosize_reset', scalar => 'max_autosize', scalar => 'autosize_increment', scalar => 'files', scalar => 'files_used', scalar => 'maxdir_size', scalar => 'space_guarantee', scalar => 'space_guarantee_enabled', scalar => 'min_readahead', scalar => 'atime_update', scalar => 'snapdir_access', scalar => 'percent_snapshot_space', scalar => 'snapshot_space_used', scalar => 'snapshot_policy', scalar => 'create_time', scalar => 'language', scalar => 'stripe_count', scalar => 'stripe_width', scalar => 'striping_epoch', scalar => 'properly_striped', scalar => 'concurrency', scalar => 'optimize', scalar => 'clone_volume', scalar => 'antivirus_on_access_policy', scalar => 'uuid', scalar => 'stripe_format', scalar => 'index_dir_enabled', scalar => 'load_sharing_source', scalar => 'move_target', scalar => 'max_write_alloc_blocks', scalar => 'is_inconsistent', scalar => 'overwrite_reserve', scalar => 'physical_replica', scalar => 'foreground', scalar => 'flexcache_cache_policy', scalar => 'flexcahce_fill_policy', scalar => 'nvfail', scalar => 'in_nvfailed_state', scalar => 'filesys_size_fixed', scalar => 'fractional_reserve', scalar => 'create_ucode', scalar => 'convert_ucode', scalar => 'space_mgmt_try_first', scalar => 'is_quiesced_on_disk', scalar => 'is_quiesced_in_memory', scalar => 'transition_state', scalar => 'is_copied_for_transition', scalar => 'is_transitioned', scalar => 'is_sis_volume', scalar => 'is_sis_logging_enabled', scalar => 'sis_space_saved', scalar => 'sis_space_saved_percent', scalar => 'dedupe_space_saved_percent', scalar => 'dedupe_space_saved', scalar => 'compression_space_saved', scalar => 'compression_space_saved_percent', scalar => 'instance_uuid', scalar => 'provenance_uuid', scalar => 'block_type', scalar => 'flexcache_origin_volume', scalar => 'inodefile_public_capacity', scalar => 'files_private_used', scalar => 'inodefile_private_capacity', scalar => 'node', scalar => 'clone_parent_name', scalar => 'clone_parent_bsnap_name', scalar => 'clone_child_count', scalar => 'clone_parent_bsnap_id', scalar => 'clone_parent_dsid', scalar => 'clone_parent_msid', scalar => 'clone_parent_uuid', scalar => 'inode_parent_path_trans_enabled', scalar => 'extent_enabled', scalar => 'overwrite_reserve_required', scalar => 'overwrite_reserve_used', scalar => 'overwrite_reserve_used_actual', scalar => 'auto_snapshots_enabled', scalar => 'snap_mirror_enabled', scalar => 'ignore_inconsistent', scalar => 'fsid', scalar => 'oem_character_set', scalar => 'nfs_character_set', scalar => 'snapshot_clone_dependency', scalar => 'svo_enabled', scalar => 'svo_checksum_enabled', scalar => 'svo_reject_errors', scalar => 'svo_rman_allowed', scalar => 'disk_root', scalar => 'is_mroot', scalar => 'compression_enabled', scalar => 'read_realloc', scalar => 'root_dir_gen', scalar => 'sched_snap_name', scalar => 'volume_check_status', scalar => 'volume_check_estimated_time_left', scalar => 'snap_autodelete_enabled', scalar => 'snap_autodelete_commitment', scalar => 'snap_autodelete_defer_delete', scalar => 'snap_autodelete_delete_order', scalar => 'snap_autodelete_prefix', scalar => 'snap_autodelete_target_free_space', scalar => 'snap_autodelete_trigger', scalar => 'snap_autodelete_destroy_list', scalar => 'is_invalid', scalar => 'is_unrecoverable', scalar => 'containing_aggregate_uuid', scalar => 'vserver_uuid', scalar => 'redirect_snapshot_id', scalar => 'is_data_protection_mirror', scalar => 'is_load_sharing_mirror', scalar => 'is_move_mirror', scalar => 'is_replica_volume', scalar => 'mirror_transfer_in_progress', scalar => 'size_available_for_snapshots', scalar => 'snapshot_reserve_size', scalar => 'is_readonly', scalar => 'is_cluster_volume', scalar => 'snaplock_type', scalar => 'snaplock_default_period', scalar => 'snaplock_maximum_period', scalar => 'snaplock_minimum_period', scalar => 'snaplock_autocommit_period', scalar => 'vserver_dr_protection', scalar => [ { '-default_ctor' => sub { return $_[0]->language(); } }, 'language_str' ], scalar => 'volume_style_extended', scalar => 'default_extent_size', scalar => 'encrypt', # specific to 7Mode only scalar => 'svo_allow_rman', scalar => 'compression', scalar => 'nosnapdir', scalar => 'svo_checksum', scalar => 'svo_enable', scalar => 'nbu_archival_snap', scalar => 'extent', scalar => 'no_atime_update', scalar => 'try_first', scalar => 'fs_size_fixed', scalar => 'schedsnapname', scalar => 'nosnap', scalar => 'guarantee', scalar => 'snapmirrored', scalar => 'no_i2p', array => 'plexes', scalar => 'plex_count', scalar => 'chksumstyle', scalar => 'chksumstatus', scalar => 'chksumenabled', array => 'disklist', scalar => 'diskcount', scalar => 'mirror_status', scalar => 'is_snaplock', scalar => 'raidsize', scalar => 'size_used', scalar => 'inconsistent', scalar => 'raidstatus', scalar => 'root', scalar => 'upgrade_64bit_estimate_age', scalar => 'upgrade_64bit_check_last_errno', scalar => 'upgrade_64bit_available_space', scalar => 'size_used_by_snapshots', scalar => 'is_compressed_volume', scalar => 'upgrade_64bit_grow_vol_size_by', scalar => 'upgrade_64bit_used_space', scalar => 'flexcache_connection_status', scalar => 'files_maximum_possible', scalar => 'upgrade_64bit_start_last_errno', scalar => 'dedupe_space_shared', scalar => 'upgrade_64bit_percent_used_space', scalar => 'flexcache_fill_policy', scalar => 'constituent_role', scalar => 'used_afs', scalar => 'directory_bitwidth', scalar => 'junction_parent', scalar => 'ignore_repository_check', scalar => 'is_constituent', scalar => 'is_read_only', scalar => 'raid_type', scalar => 'mirror_type', array => 'clones_list', scalar => 'flexcache_min_reserve', scalar => 'vm_align_suffix', scalar => 'is_moving', scalar => 'vm_align_sector', scalar => 'transition_behavior', scalar => 'hybrid_cache_write_caching_ineligibility_reason', scalar => 'hybrid_cache_eligibility', scalar => 'vbn_zero_space_saved', scalar => 'vbn_zero_space_saved_percent', scalar => 'force_nvfail_on_dr', scalar => 'autosize_grow_threshold_percent', scalar => 'autosize_mode', scalar => 'min_autosize', scalar => 'autosize_shrink_threshold_percent', scalar => 'object_write_sync_period', scalar => 'is_reserve_constituent', scalar => 'enable_object_store', array => 'ols_aggr_list', scalar => 'ols_constituent_count', scalar => 'ols_constituent_size', scalar => 'qos_policy_group', scalar => 'is_managed_by_service', scalar => 'enable_snapdiff', scalar => 'storage_service', scalar => 'is_volume_in_cutover', scalar => 'flexvol_metadata_fp', scalar => 'user_data_percent', scalar => 'quota_metafiles', scalar => 'volume_guarantee_fp_percent', scalar => 'fc_delegs_enabled', scalar => 'snapmirror_destination_fp_percent', scalar => 'quota_metafiles_percent', scalar => 'volume_data_fp_percent', scalar => 'snapshot_spill', scalar => 'dedupe_metafiles_tmp_fp_percent', scalar => 'inodes', scalar => 'dedupe_metafiles', scalar => 'user_data', scalar => 'dedupe_metafiles_percent', scalar => 'tape_backup_metafiles_fp', scalar => 'volume_data_fp', scalar => 'dedupe_metafiles_fp_percent', scalar => 'dedupe_metafiles_tmp_percent', scalar => 'autosize', scalar => 'delayed_free_fp', scalar => 'total_fp', scalar => 'tape_backup_metafiles_fp_percent', scalar => 'total_fp_percent', scalar => 'snapshot_spill_percent', scalar => 'inodes_percent', scalar => 'volume_filesystem_metadata', scalar => 'aggregate_size', scalar => 'used_including_snap_reserve', scalar => 'delayed_free_fp_percent', scalar => 'snapmirror_destination_fp', scalar => 'flexvol_metadata_fp_percent', scalar => 'dedupe_metafiles_tmp', scalar => 'dedupe_metafiles_fp', scalar => 'dedupe_metafiles_tmp_fp', scalar => 'volume_filesystem_metadata_percent', scalar => 'used_including_snap_reserve_percent', scalar => 'volume_guarantee_fp', scalar => 'external_cache', scalar => 'use_new_algorithm', scalar => 'percent_reserve_constituent', scalar => 'max_namespace_constituent_size', array => 'data_aggr_list', array => 'ns_mirror_aggr_list', scalar => 'namespace_aggregate', scalar => 'max_data_constituent_size', scalar => 'snapmirror_metadata_percent', scalar => 'snapmirror_metadata', scalar => 'space_full_threshold_percent', scalar => 'space_nearly_full_threshold_percent', scalar => 'autosize_increment_percent', scalar => 'flexgroup_uuid', scalar => 'flexgroup_msid', scalar => 'flexgroup_index', scalar => 'is_flexgroup', array => 'aggr_list', array => 'nodes', array => 'status', scalar => 'aggr_list_multiplier', #specific to CMode ZAPI scalar => 'become_node_root_after_reboot', scalar => 'percentage_snapshot_reserved', scalar => 'percentage_snapshot_reserve_used', scalar => 'upgrade_64bit_grow_space', scalar => 'upgrade_64bit_capacity', scalar => 'stripe_algorithm', scalar => 'physical_used', scalar => 'snapshot_count', scalar => 'filesystem_size', scalar => 'aggregate_size_available', scalar => 'unreachable_attr_action', scalar => 'i2p_enabled', scalar => 'caching_policy', scalar => 'snapshot_policy_uuid', scalar => 'junctionFileHandle', scalar => 'qos_policy_group_uuid', scalar => 'config_replication_flexcache_origin_volume', scalar => 'efficiency_policy', scalar => 'vbn_bad_present', scalar => 'efficiency_policy_uuid', scalar => 'excluded_from_balancer', scalar => 'clone_parent_bsnap_uuid', scalar => 'clone_parent_type', scalar => 'is_vol_on_hybrid_aggr', scalar => 'is_balance_eligible', scalar => 'raid_cv', scalar => 'thorough_scrub', scalar => 'no_delete_log', scalar => 'striping', scalar => 'actual_guarantee', scalar => 'ha_policy', scalar => 'effective_guarantee', scalar => 'dlog_hole_reserve', scalar => 'upgraded_replica', scalar => 'resyncsnaptime', scalar => 'raidtype', scalar => 'max_constituent_size', scalar => 'tiering_policy', scalar => 'qos_adaptive_policy_group', scalar => 'is_flexgroup_qtree_enabled', ]; =head1 METHODS =head2 fetch my $volume_state = NACL::CS::Volume->fetch(command_interface=>$ci,...); my @volume_states = NACL::CS::Volume->fetch(command_interface=>$ci,...); see L. Uses a CMode CLI or a CMode ZAPI or a 7Mode CLI or a Nodescope CLI or a 7Mode ZAPI APISet. This method also provides a mechanism for filtering for system volumes. "System volumes" are those which are used by the MCC Configuration Replication subsystem. These volumes are typically named MDV_mcc_crs_* and their comment field will be either of: "System volume for CPS Configuration Replication Storage Area Group subsystem." or "System volume for MetroCluster Configuration Replication subsystem." The option to be provided is C and its value is supposed to be 0|1. # Returns only system volumes. my @system_vols = NACL::CS::Volume->fetch( command_interface => $ci, %other_opts, is_system_vol => 1 ); # Returns only volumes which are NOT system volumes. my @not_system_vols = NACL::CS::Volume->fetch( command_interface => $ci, %other_opts, is_system_vol => 0 ); =over =item Exceptions =over =item C When there are no elements matching the query specified or elements of that type doesn't exist, then this exception will be thrown. =back =back =cut sub fetch { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %opts = validate @args, $pkg->_fetch_validate_spec(); my %orig_opts = %opts; my $is_system_vol = delete $opts{is_system_vol}; if (defined $is_system_vol) { # We determine this by looking at the "comment" field, so let's # add "comment" to requested_fields to ensure we get it back. if (@{$opts{requested_fields}}) { my %req_fields_hash = map { $_ => 1 } @{$opts{requested_fields}}; $req_fields_hash{comment} = 1; $opts{requested_fields} = [keys %req_fields_hash]; } } my $exception_text = 'No matching volume(s) found'; my @state_objs = $pkg->SUPER::fetch( %opts, show_cmd => 'volume show', choices => [ { method => "_fetch_cmode_cli", interface => "CLI", set => "CMode", }, { method => '_fetch_cmode_zapi', interface => 'ZAPI', set => 'CMode', check => '_cmode_zapi_check' }, { method => '_fetch_7mode_cli', interface => 'CLI', set => '7Mode|Nodescope', }, { method => '_fetch_7mode_zapi', interface => 'ZAPI', set => '7Mode', }, ], exception_text => $exception_text, ); if (defined $is_system_vol) { my @new_state_objs; # System volumes will have their comment string as either of: # System volume for CPS Configuration Replication Storage Area Group subsystem. # System volume for MetroCluster Configuration Replication subsystem. my $comment_regex = qr/System volume for/; # These volumes are supposed to begin with "MDV". A combination of the # two checks should be sufficient to ensure it is a system volume. my $name_regex = qr/^MDV/; foreach my $cs (@state_objs) { my $comment = $cs->comment(); my $volume = $cs->volume(); if ($is_system_vol) { if ($volume =~ $name_regex && $comment =~ $comment_regex) { push(@new_state_objs, $cs); } } else { if ($volume !~ $name_regex || $comment !~ $comment_regex) { push(@new_state_objs, $cs); } } } @state_objs = @new_state_objs; if (!@state_objs && !$opts{allow_empty}) { $pkg->_throw_no_elements_found( filter => $opts{filter}, exception_text => $exception_text, fetch_args => \%orig_opts ); } } $Log->exit() if $may_exit; return wantarray ? @state_objs : $state_objs[0]; } ## end sub fetch sub _fetch_cmode_cli { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my @state_objs = $pkg->SUPER::_fetch_cmode_cli(@args, api => 'volume_show',); $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_cmode_cli sub _fetch_7mode_cli { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %opts = validate @args, $pkg->_fetch_backend_validate_spec(); my $apiset = $opts{apiset}; my $ci = $opts{command_interface}; my %nodescope_override; if ($opts{command_interface}->is_cmode()) { # This is Nodescope if (defined $opts{'nodescope-node-name'}) { $nodescope_override{'nodescope-node-name'} = $opts{'nodescope-node-name'}; } } ## end if ( $opts{command_interface...}) my @orig_requested_fields = @{$opts{requested_fields}}; my %orig_filter = %{$opts{filter}}; $pkg->_remove_relational_regex_filters( filter => \%orig_filter, requested_fields => \@orig_requested_fields ); my $requested_fields = \@orig_requested_fields; my $filter = \%orig_filter; my %req_field_filter = (requested_fields => $requested_fields, filter => $filter); my (%volstatus_args, $need_verbose, $need_cluster_attributes); # The following fields can be filled in only if vol_status is invoked # in verbose: # * All the snap autodelete fields # * All of the volume options, except snapshot-clone-dependency and disk-root # * Instance-UUID and Provenance-UUID # * The containing aggregate name ('aggregate') # * The clone fields # # This section of code populates the array @vol_options_only_in_verbose # to contain these fields my @snap_autodel_fields = @{_snap_autodelete_fields()}; my %vol_options_map = %{_vol_options_map()}; my @vol_options_copy = @{_vol_options_copy()}; my @all_vol_options = ((keys %vol_options_map), @vol_options_copy); my %vol_options_in_verbose_hash; foreach my $vol_option (@all_vol_options) { $vol_options_in_verbose_hash{$vol_option} = 1; } my %vol_autosize_map = %{_vol_autosize_map()}; # _vol_autosize_map() contains the mapping for the autosize options # which are common for C and CS. There are a couple of options which # are applicable only on the CS side, which I'm adding into the hash here. $vol_autosize_map{'autosize-mode'} = 'mode'; $vol_autosize_map{'autosize'} = 'state'; my @vol_autosize_fields = keys %vol_autosize_map; # snapshot-clone-dependency and disk-root are displayed without verbose foreach my $option_present_in_normal (qw(snapshot-clone-dependency disk-root)) { delete $vol_options_in_verbose_hash{$option_present_in_normal}; } my @vol_options_only_in_verbose = keys %vol_options_in_verbose_hash; my @fields_only_in_verbose = ( @snap_autodel_fields, @vol_options_only_in_verbose, @vol_autosize_fields, ( 'instance-uuid', 'provenance-uuid', 'aggregate', 'clone-parent-name', 'clone-parent-bsnap-name', 'clone-volume', 'clones-list', 'hybrid-cache-eligibility', 'hybrid-cache-write-caching-ineligibility-reason', ) ); # Check if any of the fields filled only in verbose are required in the # state object if ($pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => \@fields_only_in_verbose, ) ) { $need_verbose = 1; } ## end if ( $pkg->_want_any_field_of...) # Check if any of the fields filled in by running with "-C" are required # These are Cluster attributes so are only available in CMode systems # (i.e through NGSH/Node-scope), so first check the hosttype if (($opts{command_interface}->mode() eq 'CMode') && ($pkg->_want_any_field_of( requested_fields => $requested_fields, filter => $filter, fields_filled_by_api => [ qw(is-transitioned is-copied-for-transition transition-state msid dsid in-nvfailed-state mirror-type) ] ) ) ) { $need_cluster_attributes = 1; } ## end if ( ( $opts{command_interface...})) if ($need_verbose && $need_cluster_attributes) { # Run with -Cv $volstatus_args{'list-verbose'} = 1; } elsif ($need_verbose) { $volstatus_args{verbose} = 1; } elsif ($need_cluster_attributes) { $volstatus_args{list} = 1; } # Provide a large timeout so that the command will not timeout if there is # a lot of output to display $volstatus_args{'connectrec-timeout'} = $opts{'method-timeout'} || 1200; # The only server-side filtering possible in 7-mode is if the # caller limits to one volume name, then we can get info on just # that volume. Otherwise, we get info on all volumes and # filter out undesired entries somewhere below. if (defined($filter->{volume})) { $volstatus_args{volume} = $filter->{volume}; } my ($response, $caught_exception); try { $Log->debug( sub { "Arguments to API 'vol_status':\n" . Dumper(\%volstatus_args); } ); $response = $apiset->vol_status(%volstatus_args, %nodescope_override); } ## end try catch NACL::APISet::Exceptions::InvalidNodescopeNodeException with { $Log->exit() if $may_exit; $_[0]->throw(); } catch NACL::APISet::Exceptions::InvalidParamValueException with { # A caught exception indicates that the volume being looked for # does not exist. We catch the exception and return immediately. The # 'fetch' frontend decides whether to throw a NoElementsFound # exception based on the value of 'allow_empty' $Log->debug("An 'InvalidParamValueException' was caught because " . "the requested volume '" . $filter->{volume} . "' does not exist"); $caught_exception = 1; }; return if ($caught_exception); my $output = $response->get_parsed_output(); my (@state_objs, %hash_keyed_by_vol); foreach my $row (@$output) { # In the mapping below, the mappings of status and options # to internal fields is to indicate that these are not # copied directly into the state object. They need to be # flattened and remapped my $state_base_field_settings = $pkg->_hash_copy( source => $row, map => { 'containing_aggregate' => 'aggregate', 'volume_uuid' => 'uuid', 'volume_provenance_uuid' => 'provenance-uuid', 'volume_instance_uuid' => 'instance-uuid', 'transitioned' => 'is-transitioned', 'copied_for_transition' => 'is-copied-for-transition', 'master_data_set_id' => 'msid', 'data_set_id' => 'dsid', 'nvfailed_state' => 'in-nvfailed-state', 'status' => '_status', 'options' => '_options', 'snapshot_autodelete_settings' => '_snap_autodel_settings', 'clones_list' => '_clones_list', 'volume_autosize_settings' => '_autosize_settings', }, copy => [ qw(volume state transition_state mirror_type clone_volume clones_list clone-parent-name clone-parent-bsnap-name snaplock-minimum-period snaplock-maximum-period snaplock-default-period) ], ); my $volume = $state_base_field_settings->{volume}; # "vol status -v" doesn't return UUID for IC # Need to run "vol vvlabel" to get that information # See burt 580947 if (!$state_base_field_settings->{uuid}) { if ($pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => ['uuid'] ) ) { my $release = $ci->version_manager() ->get_version_attribute(attribute => 'release'); if ($release =~ /Ironcity/i) { my $response = $apiset->vol_vvlabel(volume => $volume); my $parsed_output = $response->get_parsed_output(); $state_base_field_settings->{uuid} = $parsed_output->[0]{volume_uuid}; } ## end if ( $release =~ /Ironcity/i) } ## end if ( $pkg->_want_any_field_of...) } ## end if ( !$state_base_field_settings...) if (!exists $row->{options}->[0]->{root}) { $row->{options}->[0]->{root} = "false"; } # The line describing whether it's a clone volume might or might not # exist. Determine whether the line was absent because it wasn't a # clone or because this detail wasn't asked for. if (!defined $state_base_field_settings->{clone_volume}) { if ($pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => [qw(clone-volume)] ) ) { $state_base_field_settings->{clone_volume} = 'false'; $state_base_field_settings->{'clone-parent-name'} = '-'; $state_base_field_settings->{'clone-parent-bsnap-name'} = 'false'; } ## end if ( $pkg->_want_any_field_of...) } ## end if ( !defined $state_base_field_settings...) # Convert the comma-separated string into an array if (defined $state_base_field_settings->{_clones_list}) { my $_clones_list = delete $state_base_field_settings->{_clones_list}; my @clones_list = split /,\s*/, $_clones_list; $state_base_field_settings->{clones_list} = \@clones_list; } elsif ( $pkg->_want_any_field_of( requested_fields => $requested_fields, filter => $filter, fields_filled_by_api => [qw(clones-list)] ) ) { $state_base_field_settings->{clones_list} = []; } ## end elsif ( $pkg->_want_any_field_of...) # Mapping the 'status' field my $status = delete $state_base_field_settings->{_status}; if (defined $status) { # Since a trad vol will not have any parent aggregate, changing the value to undef if ($status =~ /trad/) { $state_base_field_settings->{aggregate} = undef; } $state_base_field_settings->{raidstatus} = $status; # This is probably not complete, this is just what I've noticed show # up in the "status" field if ($status =~ /(\d+\-bit)/) { $state_base_field_settings->{'block-type'} = $1; } if ($status =~ /(raid\w+)/) { $state_base_field_settings->{'raid-type'} = $1; } $pkg->_extract_data_from_raidstatus( raidstatus => $status, hashref_to_update => $state_base_field_settings ); } ## end if ( defined $status ) my $state_option_field_settings_old = {}; my $state_option_field_settings = {}; # Flatten and remap "options" hash into regular state fields if ( (exists $state_base_field_settings->{_options}) && (@{$state_base_field_settings->{_options}})) { my $_options = delete $state_base_field_settings->{_options}; my $options = $_options->[0]; # We need to temporarily keep this for backwards compatibility my $state_option_field = [ qw(fractional_reserve ignore_inconsistent create_ucode svo_allow_rman compression nosnapdir snapshot_clone_dependency svo_checksum svo_reject_errors svo_enable nbu_archival_snap extent read_realloc convert_ucode try_first fs_size_fixed schedsnapname nosnap guarantee snapmirrored no_i2p nvfail no_delete_log) ]; $state_option_field_settings_old = $pkg->_hash_copy( source => $options, copy => $state_option_field, map => { '_no_atime_update' => 'no_atime_update', 'maxdirsize' => 'maxdir_size', 'minra' => 'min_readahead', 'vmalign_sector' => 'vm_align_sector', 'vmalign_suffix' => 'vm_align_suffix' }, ); # This contains the actual mapping we should be doing $state_option_field_settings = $pkg->_hash_copy( source => $options, copy => [@vol_options_copy], map => {reverse %vol_options_map}, ); $pkg->_handle_snaplock_type( options => $options, final_hashref => $state_option_field_settings, ); # A value of "none" for the "guarantee" field in "vol status" # should set "space-guarantee" to be disabled, else its enabled if (exists $state_option_field_settings->{'space-guarantee'}) { if ($state_option_field_settings->{'space-guarantee'} eq 'none') { $state_option_field_settings->{'space-guarantee-enabled'} = 'false'; } else { $state_option_field_settings->{'space-guarantee-enabled'} = 'true'; } } ## end if ( exists $state_option_field_settings...) _translate_options_values( hashref => $state_option_field_settings, direction => 'SEVENTOCLUSTER', values_to_translate => _option_values_to_translate(mode_of_options => 'CLUSTER'), ); my $options_to_flip_then_translate = _options_to_flip(mode_of_options => 'CLUSTER'); _flip_option_values( hashref => $state_option_field_settings, mode_of_values => 'SEVEN', values_to_flip => $options_to_flip_then_translate ); _translate_options_values( hashref => $state_option_field_settings, direction => 'SEVENTOCLUSTER', values_to_translate => $options_to_flip_then_translate ); } ## end if ( ( exists $state_base_field_settings...)) my $snap_autodel_fields = {}; if (exists $state_base_field_settings->{_snap_autodel_settings}) { my $snap_autodel_output = delete $state_base_field_settings->{_snap_autodel_settings}; my $autodel_info = $snap_autodel_output->[0]; while (my ($key, $value) = each %$autodel_info) { my $field = "snap-autodelete-$key"; $snap_autodel_fields->{$field} = $value; } $snap_autodel_fields->{'snap-autodelete-enabled'} = delete $snap_autodel_fields->{'snap-autodelete-state'}; _translate_options_values( hashref => $snap_autodel_fields, direction => 'SEVENTOCLUSTER', values_to_translate => [qw(snap-autodelete-enabled)], ); } ## end if ( exists $state_base_field_settings...) my $autosize_fields = {}; if (exists $state_base_field_settings->{_autosize_settings}) { my $autosize_settings = delete $state_base_field_settings->{_autosize_settings}; my $autosize_row = $autosize_settings->[0]; $pkg->_hash_copy( source => $autosize_row, target => $autosize_fields, map => {reverse %vol_autosize_map} ); # Output might contain either autosize or autosize-mode. # For RR, the CMode and 7Mode output contain only "autosize". # For SN, the 7Mode output contains only "autosize-mode", while # the CMode output contains both "autosize" as well as # "autosize-mode". # Hence, what we need to do is: if "autosize-mode" is populated, # then fill in the value for "autosize". If # "autosize-mode" = "off", then "autosize" should be set as "true", # else it should be set as "false". if (defined $autosize_fields->{autosize}) { # RR, need to map an on/off value to true/false my $autosize = $autosize_fields->{autosize}; my %on_true = ('on' => 'true', 'off' => 'true'); if (exists $on_true{$autosize}) { $autosize = $on_true{$autosize}; $autosize_fields->{autosize} = $autosize; } else { $Log->warn('Value of autosize should have been on/off ' . "but the parser returned it as '$autosize'. " . 'Please raise a NACL burt ' . '(type=nacl;subtype=nacl_core)'); } } else { # SN, determine the value of "autosize" by looking at the # value of "autosize-mode" my $mode = $autosize_fields->{'autosize-mode'}; if (defined $mode) { my $autosize; if ($mode eq 'off') { $autosize = 'false'; } else { $autosize = 'true'; } $autosize_fields->{autosize} = $autosize; } } } ## end if ( exists $state_base_field_settings...) my $hybrid_cache_eligibility = $row->{'hybrid_cache'}->[0]{eligibility}; my $hybrid_cache_write_caching_ineligibility_reason = $row->{'hybrid_cache'} ->[0]{hybrid_cache_write_caching_ineligibility_reason}; my %final_attributes = ( %$state_base_field_settings, %$state_option_field_settings_old,, %$state_option_field_settings, %$autosize_fields, %$snap_autodel_fields, 'junction-path' => "/vol/$volume", 'hybrid-cache-eligibility' => $hybrid_cache_eligibility, 'hybrid-cache-write-caching-ineligibility-reason' => $hybrid_cache_write_caching_ineligibility_reason, ); my $obj = $pkg->new(command_interface => $opts{command_interface}); $obj->_set_fields(row => \%final_attributes); # storing the volume state objects in a hash keyed by volume name # so that later processing to add more information to state objects # would be faster. $hash_keyed_by_vol{$obj->volume()} = $obj; $obj->_update_read_realloc(); $obj->_handle_fractional_reserve(); push @state_objs, $obj; } ## end foreach my $row (@$output) # If a single volume is passed in the filter, then request details only # for that volume my (%df_args, %vol_status_args); # If a single volume is provided, then invoke the other commands only # if it is online my $should_invoke_other_commands = 1; if (defined $filter->{volume}) { my $cs = $hash_keyed_by_vol{$filter->{volume}}; if ($cs->state() eq 'online') { $df_args{name} = $filter->{volume}; $vol_status_args{volume} = $filter->{volume}; } else { $should_invoke_other_commands = 0; } } ## end if ( defined $filter->...) if ($should_invoke_other_commands) { # Call "vol status -l" and update the "language" attribute for each # of the object (which is not offline) in @state_objs based on the # output got. if ($pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => [qw(language language-str)] ) ) { my $lang_response = $apiset->vol_status( language => 1, %nodescope_override, %vol_status_args ); my $lang_output = $lang_response->get_parsed_output(); foreach my $row (@$lang_output) { my $vol_obj = $hash_keyed_by_vol{$row->{volume}}; next unless (defined $vol_obj); $vol_obj->language($row->{language}); $vol_obj->language_str($row->{language}); } ## end foreach my $row (@$lang_output) } ## end if ( $pkg->_want_any_field_of...) my %df_commands = $pkg->_handle_commands_returning_common_fields( common_fields => [qw(size total used available)], commands => { 'df' => [qw(percent-used)], 'df_r' => [qw(overwrite-reserve)], }, preferred_command => 'df', %req_field_filter, ); # Call "df" and update the "total","used","available" and # "percent_used" attributes for each of the object (which is not # offline) in @state_objs based on the output got. if (exists $df_commands{df}) { my $space_usuage_response = $apiset->df(%nodescope_override, %df_args); my $space_usuage_output = $space_usuage_response->get_parsed_output(); my %vol_hash; foreach my $row (@$space_usuage_output) { my $filesystem = $row->{filesystem}; my $volname = $pkg->_get_volume_name(filesystem => $filesystem); my $obj = $hash_keyed_by_vol{$volname}; next unless (defined $obj); if ($filesystem =~ /\.snapshot/) { # The size of the volume is the sum of the size of the # user-visible space and the snapshot space. The # user-visible space is denoted by the attribute "total" # Again, multiply by 1024 to convert to bytes my $size = $obj->total() + ($row->{kbytes} * 1024); $obj->size($size); } else { # All of the values returned in the output are in KB # CMode CLI and ZAPI both return all output values in # bytes. The multiplication by 1024 is to convert # values to bytes. $obj->total($row->{kbytes} * 1024); $obj->used($row->{used} * 1024); $obj->available($row->{avail} * 1024); $obj->percent_used($row->{capacity}); } ## end else [ if ( $filesystem =~ /\.snapshot/)] } ## end foreach my $row (@$space_usuage_output) } ## end if ( exists $df_commands...) if (exists $df_commands{df_r}) { my $overwrite_response = $apiset->df( 'scale-units' => 1, 'reserved-space' => 1, %df_args, %nodescope_override ); my $overwrite_output = $overwrite_response->get_parsed_output(); foreach my $row (@$overwrite_output) { my $filesystem = $row->{filesystem}; # Ignore snapshot lines next if ($filesystem =~ /\.snapshot/); my $volname = $pkg->_get_volume_name( filesystem => $row->{filesystem}); my $obj = $hash_keyed_by_vol{$volname}; next unless (defined $obj); my $size = $row->{reserved}; # This field could be within brackets, strip the brackets so # the multiplication below does not fail $size =~ s/^\((.*)\)$/$1/; # If $size contains '-' then it won't call overwrite_reserve if ($size !~ /-/){ $obj->overwrite_reserve( NACL::C::UnitNormalization::convert_to_bytes($size)); }; } ## end foreach my $row (@$overwrite_output) } ## end if ( exists $df_commands...) # Call "df -i" and update the "files and "files_used" attributes # for each of the object (which is not offline) in @state_objs based # on the output got. if ($pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => [qw(files files-used)] ) ) { my $inode_usuage_response = $apiset->df( 'inode-usage' => 1, %nodescope_override, %df_args ); my $inode_usuage_output = $inode_usuage_response->get_parsed_output(); foreach my $row (@$inode_usuage_output) { my $volname = $pkg->_get_volume_name( filesystem => $row->{filesystem}); my $obj = $hash_keyed_by_vol{$volname}; next unless (defined $obj); $obj->files($row->{ifree} + $row->{iused}); $obj->files_used($row->{iused}); } ## end foreach my $row (@$inode_usuage_output) } ## end if ( $pkg->_want_any_field_of...) # Call "df -S" and update the space saved by compression, dedupe # and sis (both compression and dedupe) attribute for each # of the object (which is not offline) in @state_objs based on the # output got. if ($pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => [ qw(sis-space-saved sis-space-saved-percent dedupe-space-saved dedupe-space-saved-percent compression-space-saved compression-space-saved-percent) ] ) ) { my $df_S_response = $apiset->df( 'block-sharing-and-compression-statistics' => 1, %nodescope_override, %df_args ); my $df_S_output = $df_S_response->get_parsed_output(); foreach my $row (@$df_S_output) { my $volname = $pkg->_get_volume_name( filesystem => $row->{filesystem}); my $obj = $hash_keyed_by_vol{$volname}; next unless (defined $obj); # To maintain the consistency with other size related # fields returned in this CS object, we are normalizing # these units to bytes rather than appending the unit # Note that the service doesn't support to filter the # output by specifying the size related fields. So we # don't have to handle these options for input side $obj->sis_space_saved_percent($row->{'%total-saved'}); $obj->sis_space_saved($row->{'total-saved'} * 1024); $obj->dedupe_space_saved_percent($row->{'%deduplicated'}); $obj->dedupe_space_saved($row->{'deduplicated'} * 1024); $obj->compression_space_saved_percent($row->{'%compressed'}); $obj->compression_space_saved($row->{'compressed'} * 1024); } ## end foreach my $row (@$df_S_output) } ## end if ( $pkg->_want_any_field_of...) } ## end if ($should_invoke_other_commands) # call "aggr status -r" to get the "plex-count" and "plexes" information # of trad volumes and the containing aggregates of flex volumes. if ($pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => [qw(plex-count plexes)] ) ) { my $aggr_status_response = $apiset->aggr_status('raid-disk' => 1, %nodescope_override); my $aggr_status_output = $aggr_status_response->get_parsed_output(); foreach my $aggr (@{$aggr_status_output->[0]->{aggregate}}) { my $aggr_name = $aggr->{aggregate}; foreach my $obj (@state_objs) { my $obj_aggr = $obj->aggregate(); if ( ($obj->volume() eq $aggr_name) || (defined($obj_aggr) && ($obj_aggr eq $aggr_name))) { $obj->plex_count(scalar(@{$aggr->{plex}})); # The value of "plexes" attribute is made to look similar to the # value of "plexes" field which is got when the Zapi command # "volume-list-info" is executed. my @plex_arr; foreach my $plex_info (@{$aggr->{plex}}) { my $plex; my $status = $plex_info->{status}; $plex->{'is-online'} = ($status =~ /online/) ? "true" : "false"; $plex->{status} = $plex_info->{status}; $plex->{name} = $plex_info->{plex_name}; my @raid_arr; foreach my $plex_raid (@{$plex_info->{raid_group}}) { my @disks; foreach my $plex_disk (@{$plex_raid->{disks}}) { my $disk_name; $disk_name->{name} = $plex_disk->{device}; push(@disks, $disk_name); } ## end foreach my $plex_disk ( @{ ...}) my $raid_group; $raid_group->{disks}->[0]->{'disk-info'} = \@disks; $raid_group->{name} = $plex_raid->{raid_group_name}; $raid_group->{status} = $plex_raid->{status}; push(@raid_arr, $raid_group); } ## end foreach my $plex_raid ( @{ ...}) $plex->{'raid-groups'}->[0]->{'raid-group-info'} = \@raid_arr; push(@plex_arr, $plex); } ## end foreach my $plex_info ( @{ ...}) my $_plex_info; $_plex_info->[0]->{'plex-info'} = \@plex_arr; $obj->plexes(@$_plex_info); } ## end if ( ( $obj->volume() ...)) } ## end foreach my $obj (@state_objs) } ## end foreach my $aggr ( @{ $aggr_status_output...}) } ## end if ( $pkg->_want_any_field_of...) return @state_objs; } ## end sub _fetch_7mode_cli sub _fetch_7mode_zapi { $Log->enter() if $may_enter; my $pkg = shift; my %opts = validate @_, $pkg->_fetch_backend_validate_spec(); my $apiset = $opts{apiset}; my @orig_requested_fields = @{$opts{requested_fields}}; my %orig_filter = %{$opts{filter}}; $pkg->_remove_relational_regex_filters( filter => \%orig_filter, requested_fields => \@orig_requested_fields ); my $requested_fields = \@orig_requested_fields; my $filter = \%orig_filter; my %req_field_filter = (requested_fields => $requested_fields, filter => $filter); my %vol_info_args; # When volume_list_info is invoked in verbose mode, the information it # displays which is not displayed in non-verbose mode is the disklist # and the snap autodelete fields. Only if we require any of these fields # should we invoke it in verbose mode. (This is an optimization since it # allows for smaller packets of data to be returned unless we really # require the extra details provided in verbose) if ($pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => [ ( 'disklist', 'clone-parent-name', 'clone-volume', 'clone-parent-bsnap-name', 'clones-list', 'hybrid-cache-eligibility', 'hybrid-cache-write-caching-ineligibility-reason', @{_snap_autodelete_fields()} ) ] ) ) { $vol_info_args{verbose} = 'true'; } ## end if ( $pkg->_want_any_field_of...) # The only server-side filtering possible in ZAPI is if the # caller limits to one volume name, then we can get info on just # that volume. Otherwise, we get info on all volumes and _apply_filter # will take care of filtering out unnecessary objects. if (defined $filter->{volume}) { $vol_info_args{volume} = $filter->{volume}; } my ($vol_response, $caught_exception); try { $Log->debug("Arguments to API 'volume_list_info':\n" . Dumper(\%vol_info_args)); $vol_response = $apiset->volume_list_info(%vol_info_args); } catch NACL::APISet::Exceptions::ResponseException with { # A caught exception indicates that the volume being looked for # does not exist. We catch the exception and return immediately. The # 'fetch' frontend decides whether to throw a NoElementsFound # exception based on the value of 'allow_empty' $Log->debug( 'ResponseException caught. Requested volume does ' . 'not exist'); $caught_exception = 1; }; return if ($caught_exception); my $vol_output = $vol_response->get_parsed_output(); my @state_objs; foreach my $row (@{$vol_output->[0]->{volumes}->[0]->{'volume-info'}}) { # The mapping of volume-64bit-upgrade, sis and snap-autodelete to # private fields indicates that they are not directly copied into the # state object but need to be flattened and remapped first my $state_fields = $pkg->_hash_copy( source => $row, copy => [ qw(uuid plexes state block-type instance-uuid provenance-uuid) ], map => { 'checksum-style' => 'chksumstyle', 'percentage-used' => 'percent_used', 'disk-count' => 'diskcount', 'name' => 'volume', 'files-total' => 'files', 'checksum-status' => 'chksumstatus', 'raid-size' => 'raidsize', 'size-total' => 'total', 'size-used' => 'used', 'is-inconsistent' => 'inconsistent', 'is-checksum-enabled' => 'chksumenabled', 'raid-status' => 'raidstatus', 'containing-aggregate' => 'aggregate', 'type' => 'volume-style', 'volume-64bit-upgrade' => '_upgrade-64bit-fields', 'snap-autodelete' => '_snap-autodelete-fields', 'sis' => '_sis-fields', 'clone-children' => '_clone-children', 'clone-parent' => '_clone-parent' }, ); my $volume = $state_fields->{volume}; # CMode (CLI and ZAPI) and 7Mode CLI have the value for block_type # be either of 32-bit or 64-bit, but 7Mode ZAPI has the value as # 32_bit or 64_bit. We need to translate this _ to - my $new_block_type = $state_fields->{'block-type'}; $new_block_type =~ s/_/-/; $state_fields->{'block-type'} = $new_block_type; if (defined $state_fields->{raidstatus}) { $pkg->_extract_data_from_raidstatus( raidstatus => delete $state_fields->{raidstatus}, hashref_to_update => $state_fields ); } ## end if ( defined $state_fields...) # Obtain the disk list here. # This is not guaranteed to exist (exists only if volume_list_info # was invoked in verbose), so do an existence check first if (exists $row->{plexes}[0]{'plex-info'}[0]{'raid-groups'}) { my @disklist; my $disk_info = $row->{plexes}[0]{'plex-info'}[0]{'raid-groups'}[0] {'raid-group-info'}[0]{disks}[0]{'disk-info'}; foreach my $disk (@$disk_info) { push(@disklist, $disk->{name}); } $state_fields->{disklist} = \@disklist; } ## end if ( exists $row->{plexes...}) # Flatten and remap the snap autodelete fields here. # Note that these fields are not guaranteed to exist (they will be # present only if volume_list_info was invoked with verbose), so # we first need to check if they exist. my $snap_autodel_fields = {}; if (exists $state_fields->{'_snap-autodelete-fields'}) { my $snap_autodel_base = delete $state_fields->{'_snap-autodelete-fields'}; my $autodel_info = $snap_autodel_base->[0]{'snap-autodelete-info'}[0]; while (my ($key, $value) = each %$autodel_info) { my $option_name = "snap-autodelete-$key"; $snap_autodel_fields->{$option_name} = $value; } $snap_autodel_fields->{'snap-autodelete-enabled'} = delete $snap_autodel_fields->{'snap-autodelete-state'}; _translate_options_values( hashref => $snap_autodel_fields, direction => 'SEVENTOCLUSTER', values_to_translate => [qw(snap-autodelete-enabled)], ); } ## end if ( exists $state_fields...) # Flatten and remap sis related fields here. # Note that these fields are not guaranteed to exist, so # we first need to check if they exist. my $sis_fields = {}; if (exists $state_fields->{'_sis-fields'}) { my $sis_base = delete $state_fields->{'_sis-fields'}; my $sis_info = $sis_base->[0]{'sis-info'}[0]; $sis_fields = $pkg->_hash_copy( source => $sis_info, map => { "percent-total-saved" => "sis_space_saved_percent", "percent-dedup-saved" => "dedupe_space_saved_percent", "percent-compress-saved" => "compression_space_saved_percent", }, ); # Normalize the size fields to bytes like how it is done # in other backend methods of this CS file to maintain the # consistency rather than appending the units. $sis_fields->{'sis_space_saved'} = $sis_info->{'total-saved'} * 1024; $sis_fields->{'dedupe_space_saved'} = $sis_info->{'dedup-saved'} * 1024; $sis_fields->{'compression_space_saved'} = $sis_info->{'compress-saved'} * 1024; } ## end if ( exists $state_fields...) # Extract information regarding clone-children (if any) my $clone_children = {}; if (exists $state_fields->{'_clone-children'}) { my $clone_children_base = delete $state_fields->{'_clone-children'}; my @clones; foreach my $clone_child (@$clone_children_base) { push @clones, $clone_child->{'clone-child-info'}[0]{'clone-child-name'}; } $clone_children->{clones_list} = \@clones; } elsif ( $pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => [qw(clones-list)] ) ) { # If we asked explicitly for the clone-* fields but it still # did not show up, then it means that this is not a cloned volume # First check if these fields were asked for (since if they were # not asked then they would not show up) $clone_children->{clones_list} = []; } ## end elsif ( $pkg->_want_any_field_of...) my $clone_parent = {}; if (exists $state_fields->{'_clone-parent'}) { my $clone_parent_base = delete $state_fields->{'_clone-parent'}; my $parent_info = $clone_parent_base->[0]{'clone-parent-info'}[0]; $clone_parent->{'clone-parent-name'} = $parent_info->{'parent-snapshot-name'}; $clone_parent->{'clone-parent-bsnap-name'} = $parent_info->{'parent-volume-name'}; $clone_parent->{'clone-volume'} = 'true'; } elsif ( $pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => [ qw(clone-parent-name clone-parent-bsnap-name clone-volume) ] ) ) { $clone_parent->{'clone-volume'} = 'false'; $clone_parent->{'clone-parent-name'} = '-'; $clone_parent->{'clone-parent-bsnap-name'} = '-'; } ## end elsif ( $pkg->_want_any_field_of...) # Flatten and remap the volume 64bit upgrade fields here. # XXX I don't know what the fields are called in 7Mode, so I don't # how how to map them to CMode yet. my $vol64bit_upgrade_fields = delete $state_fields->{'_upgrade-64bit-fields'}; my $vol_opt_fields = {}; my $autosize_fields = {}; # Only if the volume is online can we execute other commands # to obtain details about it if ($state_fields->{state} eq 'online') { # Obtain the language field if required by invoking # volume_get_language if ($pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => [qw(language)] ) ) { my $vol_lang_response = $apiset->volume_get_language(volume => $volume); my $vol_lang_output = $vol_lang_response->get_parsed_output(); $state_fields->{language} = $vol_lang_output->[0]->{language}; } ## end if ( $pkg->_want_any_field_of...) # Obtain the volume options here (if required) # This is done by invoking volume_options_list_info # We use helper methods written in NACL::C::Volume to help # with the mapping of field names and field values my $fields_filled_by_vol_opts_api = [(keys %{_vol_options_map()}), (@{_vol_options_copy()})]; if ($pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => $fields_filled_by_vol_opts_api ) ) { my $volopts_response = $apiset->volume_options_list_info(volume => $volume); my $volopts_parsed_output = $volopts_response->get_parsed_output(); my $vol_opt_info = $volopts_parsed_output->[0]{options}[0] {'volume-option-info'}; foreach my $each_option (@$vol_opt_info) { $vol_opt_fields->{$each_option->{name}} = $each_option->{value}; } _translate_options_values( hashref => $vol_opt_fields, direction => 'SEVENTOCLUSTER', values_to_translate => _option_values_to_translate( mode_of_options => 'SEVEN' ), ); $pkg->_hash_move( source => $vol_opt_fields, map => {reverse %{_vol_options_map()}}, target => $vol_opt_fields, ); } ## end if ( $pkg->_want_any_field_of...) # Obtain the autosize fields here (if required) # This is done by invoking volume_autosize_get if ( ($state_fields->{'volume-style'} ne 'trad') && ($state_fields->{'is-read-only'} ne 'true') && ($pkg->_want_any_field_of( %req_field_filter, fields_filled_by_api => _fields_filled_vol_autosize_api() ) ) ) { my $autosize_response = $apiset->volume_autosize_get(volume => $volume); my $autosize_rows = $autosize_response->get_parsed_output(); my $autosize_output = $autosize_rows->[0]; $pkg->_hash_copy( source => $autosize_output, target => $autosize_fields, map => { 'is-enabled' => 'autosize', 'maximum-size' => 'max-autosize', 'increment-size' => 'autosize-increment', 'minimum-size' => 'min-autosize', 'grow-threshold-percent' => 'autosize-grow-threshold-percent', 'mode' => 'autosize-mode', 'shrink-threshold-percent' => 'autosize-shrink-threshold-percent' } ); } ## end if ( ( $state_fields->...)) } ## end if ( $state_fields->{state...}) my $hybrid_cache_eligibility = $row->{'hybrid-cache-eligibility'}; my $hybrid_cache_write_caching_ineligibility_reason = $row->{'hybrid-cache-write-caching-ineligibility-reason'}; my %final_attributes = ( %$state_fields, %$vol_opt_fields, %$autosize_fields, %$snap_autodel_fields, %$sis_fields, 'junction-path' => "/vol/$volume", %$clone_children, %$clone_parent, 'hybrid-cache-eligibility' => $hybrid_cache_eligibility, 'hybrid-cache-write-caching-ineligibility-reason' => $hybrid_cache_write_caching_ineligibility_reason, ); my $obj = $pkg->new(command_interface => $opts{command_interface}); $obj->_set_fields(row => \%final_attributes); $obj->_update_read_realloc(); $obj->_handle_fractional_reserve(); push @state_objs, $obj; } ## end foreach my $row ( @{ $vol_output...}) $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_7mode_zapi sub _fetch_cmode_zapi { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %opts = validate @args, $pkg->_fetch_backend_validate_spec(); if ( $opts{'command_interface'}->version_manager()->is_version_ge( release => $NACL::Global::ONTAP_Release_Longboard_0 ) ) { if ( !(exists $opts{filter}{'volume-style-extended'}) ) { $opts{filter}{'volume-style-extended'} = '*'; } } my $map = $pkg->_common_zapi_map( command_interface => $opts{command_interface}); # The CLI has a field called "clone-volume", which describes if the volume # is a flexclone or not. The ZAPI has no such direct equivalent. However, # it does have a "volume-clone-attributes" typedef that shows up only # if the volume is a flexclone. my %want_any_field_args = ( requested_fields => $opts{requested_fields}, filter => $opts{filter}, fields_filled_by_api => ['clone-volume'], ); my (%fix_response); if ($pkg->_want_any_field_of(%want_any_field_args)) { if (@{$opts{requested_fields}}) { # Add a clone field to requested_fields so that the typedef shows up. my @req_field_copy = @{$opts{requested_fields}}; push @req_field_copy, 'clone-parent-name'; $opts{requested_fields} = \@req_field_copy; } $fix_response{fix_response} = sub { my %opts = @_; # opts{zapi} = parsed output of ZAPI; opts{cli} = mapped to CLI # (i.e. is what gets stored in the CS object) my $zapi = $opts{zapi}; my $cli = $opts{cli}; if (exists $zapi->{'volume-clone-attributes'}[0]{'volume-clone-parent-attributes'}) { $cli->{'clone-volume'} = 'true'; } else { $cli->{'clone-volume'} = 'false'; } }; } my @state_objs = $pkg->SUPER::_fetch_cmode_zapi( %opts, api => 'volume_get_iter', map => $map, %fix_response, ); # ZAPI could not have filtered for "clone-volume", so let's do it if (exists $opts{filter}->{'clone-volume'}) { $pkg->_apply_filter( filter => {'clone-volume' => $opts{filter}->{'clone-volume'}}, state_objs => \@state_objs, ); } $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_cmode_zapi sub _update_state_objs_cmode_zapi { $Log->enter() if $may_enter; my ($pkg, %opts) = @_; $pkg->SUPER::_update_state_objs_cmode_zapi( %opts, zapi_field_translations => { add_percentage => [qw(percent-used space-nearly-full-threshold-percent space-full-threshold-percent autosize-grow-threshold-percent autosize-shrink-threshold-percent percent-snapshot-space sis-space-saved-percent dedupe-space-saved-percent compression-space-saved-percent upgrade-64bit-percent-used-space fractional-reserve snap-autodelete-target-free-space snapshot-space-used) ], hyphenate_value => [qw(read-realloc constituent-role snaplock-type)], on_off_boolean => [qw(nvfail)], uc => [qw(type)], unix_perm_to_str => [qw(unix-permissions)], language => [qw(language)], }, ); $Log->exit() if $may_exit; } # These are the fields vol_autosize (7Mode CLI) and vol_autosize_get (7Mode ZAPI) # fill in sub _fields_filled_vol_autosize_api { return [ qw(autosize max-autosize autosize-increment min-autosize autosize-grow-threshold-percent autosize-mode autosize-shrink-threshold-percent) ]; } ## end sub _fields_filled_vol_autosize_api # private routine to get the volume name from filesystem sub _get_volume_name { $Log->enter() if $may_enter; my ($pkg, %args) = @_; my $volname = $args{'filesystem'}; if ($volname =~ /(?:\/vol\/)*([^\/]+)/) { $volname = $1; } else { NATE::BaseException->throw("Internal error: Volume name " . "could not be obtained from the filesystem " . "'$args{'filesystem'}'."); } $Log->exit() if $may_exit; return $volname; } ## end sub _get_volume_name # Private helper to extract the volume-style, determine # is it's a sis volume, if it's read-only and if it's a cluster volume sub _extract_data_from_raidstatus { $Log->enter() if $may_enter; my $pkg = shift; my %opts = validate @_, { raidstatus => {type => SCALAR}, hashref_to_update => {type => HASHREF} }; my $status = $opts{raidstatus}; my $hashref = $opts{hashref_to_update}; if ($status =~ /((flex)|(trad)|(striped))/) { $hashref->{'volume-style'} = $1; } if ($status =~ /sis/) { $hashref->{'is-sis-volume'} = 'true'; } else { $hashref->{'is-sis-volume'} = 'false'; } if ($status =~ /read\-only/) { $hashref->{'is-read-only'} = 'true'; } else { $hashref->{'is-read-only'} = 'false'; } if ($status =~ /cluster/) { $hashref->{'is-cluster-volume'} = 'true'; } else { $hashref->{'is-cluster-volume'} = 'false'; } $Log->exit() if $may_exit; } ## end sub _extract_data_from_raidstatus # private function to convert "space_optimized" to # "space-optimized" to match CLI output standard. sub _update_read_realloc { my ($obj) = @_; if ($obj->read_realloc_isset) { my $val = $obj->read_realloc(); $val =~ s/_/-/; $obj->read_realloc($val); } } ## end sub _update_read_realloc sub _handle_fractional_reserve { $Log->enter() if $may_enter; my $obj = $_[0]; my $reserve = $obj->fractional_reserve(); if (defined $reserve) { $reserve .= '%'; $obj->fractional_reserve($reserve); } $Log->exit() if $may_exit; } sub _cmode_zapi_check { $Log->enter() if $may_enter; my ($pkg, %opts) = @_; my $filter = $opts{filter}; my @fields = qw(directory-bitwidth unreachable-attr-action is-vol-on-hybrid-aggr vbn-bad-present move-target physical-replica transition-state junction-path-source has-conditions); my @cli_only_fields = $pkg->_invalid_fields_check(%opts,_fields=>\@fields); if (@cli_only_fields) { my $msg = "@cli_only_fields fields were provided in the filter or requested field in the call to ZAPI" . 'NACL::CS::Volume->fetch().' . ' Mapping has been dropped for this filter options. ' . 'So falling back to CLI for consistent output'; $Log->comment($msg); $Log->exit() if $may_exit; NACL::Exceptions::InvalidChoice->throw($msg); } my $type = $opts{filter}{type}; # If filter->{type} is defined, it contains "DEL" but is not "!DEL", # then we can't use ZAPI. This would include values like "DEL", "DEL*", # "DEL|RW" if (defined($type) && $type =~ /DEL/i && $type !~ /!DEL/i) { $Log->exit() if $may_exit; NACL::Exceptions::InvalidChoice->throw('Cannot use CMode-ZAPI ' . 'back-end method because filter->{type} was provided as ' . "'$type' but ZAPI does not return details of volumes of " . "type 'DEL'"); } $Log->exit() if $may_exit; } =head2 sort_by_newest my @sorted_by_newest = NACL::CS::Volume->sort_by_newest(state_objs => \@vol_cs_objs); (Applicable only for CMode) This subroutine arranges NACL::CS::Volume objects in descending order of creation time (i.e from volumes created last to volumes created earlier). Note that it if a volume was offline at the time its creation time was queried then the creation time will be unknown (the field will be returned as "-"). These volumes are placed at the end of the sorted list. This is particularly useful when it's necessary to purge volumes in the appropriate order. Purging a volume requires purging its clones (which in turn would require purging its clones), and if it is a root-volume of a vserver then it requires purging all the other volumes in the vserver first. By arranging it in descending order, we're (nearly) guaranteeing that we're purging them in the correct order. Note that this requires all state objects to have their C attribute populated. =over =item Options =over =item C<< state_objs => \@vol_cs_objs >> An array-reference of objects of type C. Each of these objects must have their C attribute populated. =back =back =head2 sort_by_oldest my @sorted_by_oldest = NACL::CS::Volume->sort_by_oldest(state_objs => \@vol_cs_objs); (Applicable only for CMode) Similar to L but with the state objects being arranged with the oldest ones listed first. Again, any volumes with create-time being "-" are placed onto the end of the list. =head2 is_infinivol my $boolean = $cs_obj->is_infinivol(); (Instance method only, applicable only for CMode) Returns a boolean describing whether that volume is an infinite volume or not. This requires the CS object's "volume_style" attribute to be populated. If it is not populated, then we cannot know of the volume is an infinite volume or not, so this method returns undef. =cut sub is_infinivol { $Log->enter() if $may_enter; my $self = $_[0]; my $volume_style = $self->volume_style(); my $val; if (defined $volume_style) { if ($volume_style eq 'infinitevol') { $val = 1; } else { $val = 0; } } else { $val = undef; } $Log->exit($val); return $val; } sub is_vsroot { $Log->enter() if $may_enter; my $self = $_[0]; my $val; my $canned_filter = $self->_canned_filter_vsroot_vols(); foreach my $key (keys %$canned_filter) { my $isset = $key . '_isset'; if (!$self->$isset()) { $Log->warn('Could not determine value of is_vroot() because the ' . "field '$key' was not defined in the CS object."); $Log->exit() if $may_exit; return undef; } } try { $self->verify_fields(%$canned_filter); $val = 1; } catch NACL::Exceptions::VerifyFailure with { $val = 0; }; $Log->exit($val); return $val; } # Make "is_system_vol" a valid option. sub _fetch_validate_spec { $Log->enter() if $may_enter; my ($pkg, @opts) = @_; my %spec = %{$pkg->SUPER::_fetch_validate_spec(@opts)}; $spec{is_system_vol} = {type => SCALAR, optional => 1}; $Log->exit() if $may_exit; return {%spec}; } =head1 CANNED FILTERS See http://brewery.netapp.com/Brewery/QA/TestAutomation/NACL/UsersGuide/ComponentLayerUsersGuide/index.html#canned-filters for a description of canned filters. =head2 vsroot_vols Returns vserver root volumes. =cut # Note that LS-mirrors of the root volume will also have their vsroot as true, # so that filter alone will return not just the vserver root, but also any # LS-mirrors of it. To get back ONLY the vserver root, we should make sure # the volume type is not LS. sub _canned_filter_vsroot_vols { return {vsroot => 'true', type => '!LS'}; } 1;