# $Id$ # # Copyright (c) 2001-2010 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary Vserver ComponentState Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here =head1 NAME NACL::CS::Vserver =head1 DESCRIPTION C is a derived class of L. It represents the state of an ONTAP Vserver. A related class is L, which represents access to an ONTAP Vserver. =head1 ATTRIBUTES The individual pieces of data that are part of the state of the Vserver element are the attributes of the Vserver ComponentState. =over =item C<< vserver >> Filled in for CMode CLI,SNMP,ZAPI,GUI Maps to: CMode SNMP : vserverName The name of the vserver element whose state is being represented. =item C<< type >> Filled in for CMode CLI,SNMP,ZAPI,GUI Maps to: CMode SNMP : vserverType Vserver Type =item C<< uuid >> Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverUuid =item C<< rootvolume >> Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverRootVolume =item C<< aggregate >> Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverAggregate =item C<< "ns_switch" >> (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $ns_switch = $obj->ns_switch(); $ns_switch contains a reference to the array of values my @ns_switch = $obj->ns_switch(); @ns_switch contains the array of values Filled in for CMode CLI,SNMP,ZAPI,GUI Maps to: CMode SNMP : vserverNsSwitch =item C<< "nm_switch" >> (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $nm_switch = $obj->nm_switch(); $nm_switch contains a reference to the array of values my @nm_switch = $obj->nm_switch(); @nm_switch contains the array of values Filled in for CMode CLI,SNMP,ZAPI,GUI Maps to: CMode SNMP : vserverNmSwitch =item C<< "nisdomain" >> Filled in for CMode CLI,GUI =item C<< "rootvolume_security_style" >> Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverRootVolumeSecurityStyle =item C<< "ldap_client" >> Filled in for CMode CLI,ZAPI =item C<< language >> Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverLanguage =item C<< "language_str" >> =item C<< "snapshot_policy" >> Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverSnapshotPolicy =item C<< comment >> Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverComment =item C<< "antivirus_on_access_policy" >> Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverAntiVirusOnAccessPolicy =item C<< "quota_policy" >> Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverQuotaPolicy =item C<< aggr_list >> Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverAggrList (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $aggr_list = $obj->aggr_list(); $aggr_list contains a reference to the array of values my @aggr_list = $obj->aggr_list(); @aggr_list contains the array of values =item C<< protocols >> (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $protocols = $obj->protocols(); $protocols contains a reference to the array of values my @protocols = $obj->protocols(); @protocols contains the array of values =item C<< allowed_protocols >> (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $allowed_protocols = $obj->allowed_protocols(); $allowed_protocols contains a reference to the array of values my @allowed_protocols = $obj->allowed_protocols(); @allowed_protocols contains the array of values Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverAllowedProtocolList =item C<< disallowed_protocols >> (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $disallowed_protocols = $obj->disallowed_protocols(); $disallowed_protocols contains a reference to the array of values my @disallowed_protocols = $obj->disallowed_protocols(); @disallowed_protocols contains the array of values Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverDisallowedProtocolList =item C<< protocol_services_use_data_lifs >> Filled in for CMode CLI =item C<< aggr_availsize_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 $aggr_availsize_list = $obj->aggr_availsize_list(); $aggr_availsize_list contains a reference to the array of values my @aggr_availsize_list = $obj->aggr_availsize_list(); @aggr_availsize_list contains the array of values =item C<< id >> Filled in for CMode CLI,SNMP Maps to: CMode SNMP : vserverIndex =item C<< vol_count >> Filled in for CMode CLI,SNMP Maps to: CMode SNMP : vserverNumVolumes =item C<< maxsshsessions >> Filled in for CMode CLI,ZAPI =item C<< maxStorage >> Filled in for CMode CLI,ZAPI =item C<< is_repository >> Filled in for CMode CLI and CMode ZAPI. =item C<< uuid_str >> Filled in for CMode CLI =item C<< external_uuid >> Filled in for CMode CLI =item C<< ipspace >> Filled in for CMode CLI,ZAPI =item C<< qos_policy_group >> Filled in for CMode CLI,ZAPI =item C<< external_cache >> Filled in for CMode CLI,ZAPI External Cache Policy =item C<< max_volumes >> Limit on Maximum Number of Volumes allowed Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverMaxVolumes =item C<< admin_state >> State of the vserver Filled in for CMode CLI,SNMP,ZAPI Maps to: CMode SNMP : vserverState =item C<< subtype >> Vserver subtype Filled in for CMode CLI|GUI. =item C<< caching_policy >> Caching Policy Name Filled in for CMode CLI. =item C<< operational_state_stopped_reason >> Vserver Operational State Stopped Reason Filled in for CMode CLI. =item C<< config_lock >> Config Lock possible value(s) are, true,false Filled in for CMode CLI. =item C<< operational_state >> Vserver Operational State possible value(s) are, running,stopped Filled in for CMode CLI. =item C<< snapshot_policy_uuid >> Snapshot Policy UUID Filled in for CMode CLI. =item C<< volume_delete_retention_hours >> Volume Delete Retention Period Filled in for CMode CLI/ZAPI. =item C<< qos_policy_group_uuid >> QoS Policy Group UUID Filled in for CMode CLI. =item C<< is_protected >> Is Vserver protected possible value(s) are, true,false Filled in for CMode CLI. =item C<< protocol >> Protocols possible value(s) are, nfs,cifs,fcp,iscsi,ndmp (Array) Note that for array fields, the accessor method can be invoked in either scalar or list context. my $protocol = $obj->protocol(); # $protocol contains a reference to the array of values my @protocol = $obj->protocol(); # @protocol 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 => { protocol = [ value1, value2...] } Filled in for CMode CLI. =item C<< uuidforEngineID >> UUID for generating EngineID Filled in for CMode CLI. =back =cut package NACL::CS::Vserver; use strict; use warnings; use Params::Validate qw(validate validate_with HASHREF SCALAR); use NACL::ComponentUtils qw(_dump_one _base_check_gui); use NATE::Log qw(log_global); my $Log = log_global(); my $may_enter = $Log->may_enter(); my $may_exit = $Log->may_exit(); use base qw(NACL::CS::ComponentState::ONTAP); use NACL::C::VserverServicesNameServiceNsSwitch; use NACL::Exceptions::NoElementsFound qw(:try); use NACL::C::_Mixins::Vserver qw(:all); use NACL::C::VserverPeer; use feature 'state'; use Class::MethodMaker [ scalar => 'vserver', scalar => 'type', scalar => 'uuid', scalar => 'rootvolume', scalar => 'aggregate', array => [ { '-read_cb' => sub { my ($self, $val) = @_; return () if (!ref $self); if (!$self->command_interface()->isa('NACL::C::Vserver') && $self->command_interface()->has_uichange( uichange => 'ns-switch-ui', throw_exception => 0 ) ) { $Log->warn("The value of ns-switch cannot be obtained"); } } }, 'ns_switch' ], array => [ { '-read_cb' => sub { my ($self, $val) = @_; return () if (!ref $self); if (!$self->command_interface()->isa('NACL::C::Vserver') && $self->command_interface()->has_uichange( uichange => 'ns-switch-ui', throw_exception => 0 ) ) { $Log->warn( "The fields nm-switch is not present in the output of " . ", We have given this redirection " . "as a temporary workaround, script needs to be updated to " . "call NACL::CS::VserverServicesNameServiceNsSwitch->fetch " . " instead, because we will remove this workaround soon" ); } return $val; } }, 'nm_switch' ], scalar => 'nisdomain', scalar => 'rootvolume_security_style', scalar => 'ldap_client', scalar => 'language', scalar => 'language_str', scalar => 'snapshot_policy', scalar => 'comment', scalar => 'antivirus_on_access_policy', scalar => 'quota_policy', array => 'protocols', array => 'allowed_protocols', array => 'disallowed_protocols', scalar => 'admin_state', scalar => 'max_volumes', array => 'aggr_list', scalar => 'protocol_services_use_data_lifs', array => 'aggr_availsize_list', scalar => 'id', scalar => 'vol_count', scalar => 'maxsshsessions', scalar => 'maxStorage', scalar => 'is_repository', scalar => 'uuid_str', scalar => 'external_uuid', scalar => 'ipspace', scalar => 'qos_policy_group', scalar => 'external_cache', scalar => 'subtype', scalar => 'caching_policy', scalar => 'operational_state_stopped_reason', scalar => 'config_lock', scalar => 'operational_state', scalar => 'snapshot_policy_uuid', scalar => 'volume_delete_retention_hours', scalar => 'qos_policy_group_uuid', scalar => 'is_protected', array => 'protocol', scalar => 'uuidforEngineID', ]; =head1 METHODS =head2 fetch The method is supported in CMode for CLI, ZAPI and SNMP interfaces. my $vserver_state = NACL::CS::Vserver->fetch(command_interface=>$ci,...); my @vserver_states = NACL::CS::Vserver->fetch(command_interface=>$ci,...); see L Uses a CMode CLI/ZAPI/SNMP APISet. For CMode SNMP, it reads vserverTable. =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 $is_in_peer = delete $opts{is_in_peer}; my ($filter, $requested_field, @nm_switch, $new_filter); my %orig_filter = %{$opts{filter}}; if (!$opts{command_interface}->isa('NACL::C::Vserver')) { # Handle the fields for ns-switch changes my $requested_fields = delete $opts{requested_fields}; my %copy_filter = %{$opts{filter}}; my %requested_fields_hash = map { $_ => 1 } @$requested_fields; if ( $requested_fields_hash{'ns-switch'} || $opts{filter}->{'ns-switch'}) { $Log->warn("The fields ns-switch is not present in the output of " . ",script needs to be updated to " . "call NACL::CS::VserverServicesNameServiceNsSwitch->fetch() instead" ); delete $requested_fields_hash{'ns-switch'}; delete $copy_filter{'ns-switch'}; } if ( $opts{filter}->{'nm-switch'} || $requested_fields_hash{'nm-switch'}) { $Log->warn("The field nm-switch is not present in the output of" . " , We have given this redirection" . " as a temporary workaround,Script needs to be updated to" . " call NACL::CS::VserverServicesNameServiceNsSwitch->fetch()" . " instead, because we will remove this workaround soon. See" . " http://wikid.netapp.com/w/MIG/QA/NameServices/FS.0_Automation" . " for complete details."); #Delete invalid filter fields $filter = 1; delete $copy_filter{'nm-switch'}; $opts{filter} = \%copy_filter; #Delete invalid requested fields delete $requested_fields_hash{'nm-switch'}; } my @new_requested_fields = keys %requested_fields_hash; $opts{requested_fields} = \@new_requested_fields; } $new_filter = $pkg->_convert_to_cluster_data(filter => $opts{filter}); my @state_objs = $pkg->SUPER::fetch( %opts, show_cmd => 'vserver show', filter => $new_filter, choices => [ { method => "_fetch_cmode_cli", interface => "CLI", set => "CMode", }, { method => "_fetch_cmode_zapi", interface => "ZAPI", set => "CMode", }, { method => "_fetch_cmode_snmp", check => '_fetch_cmode_snmp_check', interface => "SNMP", set => "CMode", }, { method => "_fetch_cmode_gui", interface => "GUI", set => "CMode", check => '_check_gui', }, ], exception_text => 'No matching vserver(s) found' ); if ($filter && @state_objs) { my %common_opts_with_ci; $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts_with_ci, ); my @vservers = map { $_->vserver() } @state_objs; my $vserver_list = join '|', @vservers; my %command_filter; if (defined $orig_filter{'nm-switch'}) { $command_filter{sources} = $orig_filter{'nm-switch'}; } my @ns_objs = NACL::CS::VserverServicesNameServiceNsSwitch->fetch( %common_opts_with_ci, filter => { database => 'namemap', vserver => $vserver_list, %command_filter }, allow_empty => 1 ); my %ns_switch_hash = map { $_->vserver() => $_ } @ns_objs; my @matching_cs; foreach my $state_obj_vs (@state_objs) { my $ns_obj_vs = $ns_switch_hash{$state_obj_vs->vserver()}; if (defined $ns_obj_vs) { my @sources = $ns_obj_vs->sources(); $state_obj_vs->nm_switch(@sources); push @matching_cs, $state_obj_vs; } } @state_objs = @matching_cs; } if (!$opts{allow_empty} && !@state_objs) { $pkg->_throw_no_elements_found( filter => \%orig_filter, exception_text => 'No matching vserver(s) found' ); } if (defined($is_in_peer) && @state_objs) { my %peer_filter; if ($is_in_peer) { my @vservers = map { $_->vserver() } @state_objs; $peer_filter{vserver} = join '|', @vservers; } my %common_opts_ci; $pkg->_copy_common_component_params_with_ci( source => \%opts, target => \%common_opts_ci ); my @peers = NACL::C::VserverPeer->find( %common_opts_ci, filter => \%peer_filter, allow_empty => 1 ); my @filtered_state_objs; if ($is_in_peer) { my %state_objs_by_vs = map { $_->vserver => $_ } @state_objs; @filtered_state_objs = map { $state_objs_by_vs{$_->vserver()} } @peers; } else { my %peer_objs_by_vs = map { $_->vserver => $_ } @peers; foreach my $vs_obj (@state_objs) { unless (exists $peer_objs_by_vs{$vs_obj->vserver()}) { push @filtered_state_objs, $vs_obj; } } } @state_objs = @filtered_state_objs; if (!$opts{allow_empty} && !@state_objs) { my $not = $is_in_peer ? ' ' : ' not '; $Log->exit() if $may_exit; NACL::Exceptions::NoElementsFound->throw( 'No vservers found matching ' . "the filter:\n" . _dump_one($opts{filter}) . "\nand which are${not}in a vserver peer relationship"); } } $Log->exit() if $may_exit; return wantarray ? @state_objs : $state_objs[0]; } sub _fetch_cmode_cli { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my @state_objs = $pkg->SUPER::_fetch_cmode_cli(@args, api => 'vserver_show'); $Log->exit() if $may_exit; return @state_objs; } sub _fetch_cmode_zapi { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my $common_copy = $pkg->_common_zapi_copy(); my $common_map = $pkg->_common_zapi_map(); my @state_objs = $pkg->SUPER::_fetch_cmode_zapi( @args, api => 'vserver-get-iter', map => $common_map, copy => $common_copy ); $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_cmode_zapi sub _check_gui { $Log->enter() if $may_enter; my ($pkg, %opts) = @_; $pkg->_base_check_gui(%opts, _primary_keys => [qw(vserver)]); $Log->exit() if $may_exit; } sub _fetch_cmode_gui { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my $map = { 'vserver-name' => "vserver", 'volume-type' => "type", 'vserver-subtype' => "subtype", 'name-service-switch' => "ns-switch", 'name-mapping-switch' => "nm-switch", 'nis-domain' => "nisdomain", 'is-repository-vserver' => "is-repository", }; my $copy = [qw( admin-state allowed-protocols ldap-client operational-state ipspace)]; my @state_objs = $pkg->SUPER::_fetch_cmode_gui( @args, map => $map, copy => $copy, api => 'getVserverDetails', _primary_keys => [qw(vserver)], ); $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_cmode_gui 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 => { hyphenate_value => [qw(operational-state-stopped-reason subtype allowed-protocols disallowed-protocols)], language => [qw(language)], }, ); $Log->exit() if $may_exit; } sub _fetch_cmode_snmp { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my $map = { "vserver" => 'vserverName', "type" => 'vserverType', "uuid" => 'vserverUuid', "rootvolume" => 'vserverRootVolume', "aggregate" => 'vserverAggregate', "rootvolume-security-style" => 'vserverRootVolumeSecurityStyle', "language" => 'vserverLanguage', "snapshot-policy" => 'vserverSnapshotPolicy', "comment" => 'vserverComment', "antivirus-on-access-policy" => 'vserverAntiVirusOnAccessPolicy', "quota-policy" => 'vserverQuotaPolicy', "allowed-protocols" => 'vserverAllowedProtocolList', "disallowed-protocols" => 'vserverDisallowedProtocolList', "ns-switch" => 'vserverNsSwitch', "aggr-list" => 'vserverAggrList', "max-volumes" => 'vserverMaxVolumes', "admin-state" => 'vserverState', "id" => 'vserverIndex', "vol-count" => 'vserverNumVolumes', }; my @state_objs = $pkg->SUPER::_fetch_snmp( @args, map => $map, baseoid => "vserverTable" ); $Log->exit() if $may_exit; return @state_objs; } ## end sub _fetch_cmode_snmp sub _fetch_cmode_snmp_check { $Log->enter() if $may_enter; my ($pkg, @opts) = @_; state $unsupported = [qw(is-repository)]; my @fields = $pkg->_invalid_fields_check(@opts, _fields => $unsupported); if (@fields) { $Log->exit() if $may_exit; NACL::Exceptions::InvalidChoice->throw('Could not use SNMP ' . 'since required_fields or filter specifies the following ' . 'unsupported attributes: ' . join(', ', @fields) . "\n"); } $Log->exit() if $may_exit; } ## end sub _fetch_cmode_snmp_check =head1 CANNED FILTERS See http://wikid.netapp.com/w/QA/projects/Libraries_Initiative/Project/Common_Infrastructure/Users_Guide/Component_Layer_Users_Guide#Canned_filters for a description of canned filters. =head2 data_vservers Returns data vservers, i.e. those that have type as "cluster" or "data". =head2 repository_vservers Returns repository vservers, i.e. those that have their "is-repository" field being "true". =head2 non_repository_vservers Returns non-repository vservers, i.e. those that have their "is-repository" fields being "false". This will include node and admin vservers. =head2 data_non_repository_vservers Returns vservers with are both data vservers as well as non-repository vservers. =cut sub _canned_filter_data_vservers { return {type => 'data'}; } sub _canned_filter_repository_vservers { return {'is-repository' => 'true'}; } sub _canned_filter_non_repository_vservers { return {'is-repository' => 'false'}; } sub _canned_filter_data_non_repository_vservers { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my $data_hashref = $pkg->_canned_filter_data_vservers(@args); my $non_repo_hashref = $pkg->_canned_filter_non_repository_vservers(@args); my $combo = {%$data_hashref, %$non_repo_hashref}; $Log->exit() if $may_exit; return $combo; } # In different versions and interfaces, the value of the "type" field is # either "cluster" or "data". This makes _apply_filter be able to handle # any such combination sub _apply_filter { $Log->enter() if $may_enter; my ($pkg, %opts) = @_; my $new_filter = $pkg->_convert_to_cluster_data(filter => $opts{filter}); $pkg->SUPER::_apply_filter(%opts, filter => $new_filter); $Log->exit() if $may_exit; } # If filter->type is provided and its value is either cluster or data, then # convert it to 'cluster|data'. Accepts "filter" as input, it makes a deep # copy of it, performs the translation and returns a reference to this # new hash. sub _convert_to_cluster_data { $Log->enter() if $may_enter; my ($pkg, @args) = @_; my %opts = validate_with( params => \@args, spec => {filter => {type => HASHREF}} ); my %filter = %{$opts{filter}}; if (defined $filter{type}) { if ($filter{type} eq 'cluster' || $filter{type} eq 'data') { $filter{type} = 'data'; } } $Log->exit() if $may_exit; return \%filter; } sub _fetch_validate_spec { $Log->enter() if $may_enter; my $pkg = $_[0]; my $spec = $pkg->SUPER::_fetch_validate_spec(); $spec->{is_in_peer} = {type => SCALAR, optional => 1}; $Log->exit() if $may_exit; return $spec; } 1;