# Copyright (c) 2014 NetApp, Inc., All Rights Reserved. # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @pod here =head1 NAME NACL::ServiceAPI::Discover - Discovery client code =head1 SYNOPSIS use NACL::ServiceAPI::Discover; my $dsc = NACL::ServiceAPI::Discover->new(); =cut package NACL::ServiceAPI::Discover; use strict; use warnings; use parent qw(NACL::ServiceAPI); use NATE::Log qw(log_global); use NATE::Inc qw(find_file); use NATE::ServiceAPI::Resource qw(resource_global); use XML::Simple; use Data::Dumper::Concise; use Params::Validate qw(validate_with BOOLEAN HASHREF OBJECT ARRAYREF SCALAR UNDEF); $Data::Dumper::Sortkeys = 1; my $paramset = NATE::ParamSet->get_global(); my $log = log_global(); =head1 METHODS =head2 discover Sends discovery requests to the C service =over =item Synopsis # ONTAP and user discovery my $nacl_objects = { 'Aggregate' => {}, 'Cluster' => {}, 'ClusterIdentity' => {}, 'ExportPolicy' => {}, 'ExportPolicyRule' => {}, 'License' => {}, 'Lun' => {}, 'LunBind' => {}, 'LunIgroup' => {}, 'LunMapped' => {}, 'LunPortset' => {}, 'NetworkInterface' => { 'attributes' => { 'address' => {}, 'address_family' => {}, 'curr_node' => {}, 'curr_port' => {}, 'data_protocol' => {}, 'is_home' => {}, 'lif' => {}, 'vserver' => {} } }, 'NetworkPort' => {}, 'NetworkPortIfgrp' => {}, 'NetworkRoute' => {}, 'QosPolicyGroup' => {}, 'QosWorkload' => {}, 'Quota' => {}, 'QuotaPolicy' => {}, 'QuotaPolicyRule' => {}, 'SecurityCertificate' => {'ignored_attributes' => {'public_cert' => {}}}, 'SecurityLoginBanner' => {}, 'Volume' => {}, 'VolumeQtree' => {}, 'Vserver' => {}, 'VserverAudit' => {}, 'VserverCifs' => {}, 'VserverCifsDomainPreferredDc' => {}, 'VserverCifsHomeDirectorySearchPath' => {}, 'VserverCifsSecurity' => {}, 'VserverCifsShare' => {}, 'VserverCifsUsersAndGroupsLocalUser' => {}, 'VserverExportPolicy' => {}, 'VserverExportPolicyRule' => {}, 'VserverFcp' => {}, 'VserverIscsi' => {}, 'VserverNameMapping' => {}, 'VserverNfs' => {}, 'VserverNfsKerberosConfig' => {}, 'VserverNfsKerberosInterface' => {}, 'VserverNfsKerberosRealm' => {}, 'VserverServicesLdap' => {}, 'VserverServicesLdapClient' => {}, 'VserverServicesNameServiceDns' => {}, 'VserverServicesNameServiceDnsDynamicUpdate' => {}, 'VserverServicesNameServiceLdap' => {}, 'VserverServicesNameServiceLdapClient' => {}, 'VserverServicesNameServiceNisDomain' => {}, 'VserverServicesNameServiceNsSwitch' => {}, 'VserverServicesNameServiceUnixGroup' => {}, 'VserverServicesNameServiceUnixUser' => {} }; my $user_specifications = { 'group1' => { 'users' => { 'ad' => 'bs4384jih235853.com', 'count' => '100', 'host' => '', 'hosttype' => '', 'nis' => 'bs4384jih235853.com', 'password' => 'netapp1!', 'protocol' => 'cifs,nfs', 'user_name' => 'bs4384jih235853u' } }, 'group2' => { 'users' => { 'ad' => 'bs4384jih235853.com', 'startcount' => '101', 'count' => '100', 'host' => '', 'hosttype' => '', 'nis' => 'bs4384jih235853.com', 'password' => 'netapp1!', 'protocol' => 'cifs,nfs', 'user_name' => 'bs4384jih235853u' } }, 'group3' => { 'users' => { 'ad' => 'bs4384jih235853.com', 'host' => '', 'hosttype' => '', 'nis' => '', 'password' => 'netapp', 'protocol' => 'cifs', 'user_name' => 'Administrator' } }, 'group4' => { 'users' => { 'ad' => 'bs4384jih235853.com', 'count' => '100', 'host' => '', 'hosttype' => '', 'nis' => 'bs4384jih235853.com', 'password' => 'netapp1!', 'protocol' => 'cifs,nfs', 'user_name' => 'homediru' } }, }; my %args = ( discover_homedirs => 1, discover_local_cifs_users => 1, discover_local_unix_users => 1, nacl_objects => $nacl_objects, request_timeout => -1, user_specifications => $user_specifications ); $dsc->discover(%args); # Limited ONTAP discovery my $nacl_objects = { NetworkInterface => { attributes => [ 'vserver', 'lif', 'address_family', 'address', 'curr_node', 'curr_port', 'is_home', 'data_protocol', ], }, SecurityCertificate => {ignored_attributes => ['public_cert']} }; $dsc->discover(%args); =item Arguments =over =item C< $scalar>> Make the discovery request blocking (-1) =item C< $hashref>> A hashref that defined the NACL objects to discover and their attributes to update =item C< $boolean>> (Optional) Discover qtrees/directories in CIFS home-direcotry search paths so they can be matched to local and imported users =item C< $boolean>> (Optional) Seach all clusters for CIFS local users and match them to homedirs =item C< $boolean>> (Optional) Seach all clusters for unix users =item C< $hashref>> (Optional) A hashref defining one or more userver specifications =back =back =head2 get_resources Gets resources from the resource services when there is no discovery in play =over =item Synopsis my $lifs = $dsc->get_resources(resource_type => 'NetworkInterface'); =item Arguments =over =item C< $scalar>> The resource type you want to get resources from =item C< $hashref>> (Optional) Additional Resource Service filters. The resource_type is automatically added to the filterset =item C< $scalar>> (Optional) How long you want to wait between discovery lock checks =item C< $scalar>> (Optional) The maximum time to wait for a discovery lock to be cleared =back =back =cut sub get_resources { my $self = shift; my %incoming = validate_with( params => \@_, spec => { resource_type => {type => SCALAR}, filter => {type => UNDEF | HASHREF, default => {}}, attempt_wait => {type => SCALAR, default => 5}, }, ); my $rs = resource_global(); my $max_wait = $paramset->get('GET_RESOURCES_MAX_WAIT', default => 120); my $ds_start_time = time(); $log->debug("Checking discovery lock for $incoming{resource_type}"); my $elapsed_time = 0; if (!defined($incoming{filter}->{resource_type})) { $incoming{filter}->{resource_type} = {$incoming{resource_type} => '='}; } ## end while ($elapsed_time < $max_wait) { my $lock = $rs->get_resources( filter => { resource_type => {'discover_lock' => '='}, lock => {$incoming{resource_type} => '='} } ); if (!@{$lock}) { # Say what we are doing if ($log->may_debug()) { $log->debug("Retrieving $incoming{resource_type} resources " . "matching the following filters:\n" . Dumper($incoming{filter})); } else { $log->debug("Retrieving $incoming{resource_type} resources"); } # Call the resource_services get_resources() call and time it my $resources = $rs->get_resources(filter => $incoming{filter}); # Use the methods on each of the objects to get the attributes my @array_of_attrs; if (@{$resources}) { @array_of_attrs = map { $_->attrs() } @{$resources}; } # Display how long it took for the overal retrieval of data my $ds_end_time = time(); $log->debug('NATE::ServiceAPI::Discover::get_resources took ' . ($ds_end_time - $ds_start_time) . ' seconds'); # Return the attributes $log->debug("Retrieved " . @array_of_attrs . " $incoming{resource_type} resources"); return \@array_of_attrs; } else { $log->comment( "Discovery lock exists for $incoming{resource_type}, " . "sleeping $incoming{attempt_wait}"); sleep $incoming{attempt_wait}; $elapsed_time += $incoming{attempt_wait}; } } die "Unable to get resources for $elapsed_time " . " seconds due to an existing discovery lock"; } ## end sub get_resources =head2 parse_nacl_objects_file Reads an XML file that defines the NACL objects to discover =over =item Synopsis my $nacl_objects = $dsc->parse_nacl_objects(path => 'system_test/config/discover_nacl_objects'); =item Arguments =over =item C<< path => $scalar >> The relative path to the file containing NACL objects =back =back =cut sub parse_nacl_objects_file { my $self = shift; my %incoming = validate_with( params => \@_, spec => {path => {type => SCALAR},}, ); my $path = $incoming{path}; # Determine the file location my $file = NATE::Inc::find_file($path); if (!$file) { die "Cannot find NACL objects file $path"; } # Read the XML my $xml = new XML::Simple; my $objects = $xml->XMLin($file); my $parsed_objects = {}; foreach my $key (keys(%{$objects})) { if (defined($objects->{$key}->{attributes})) { # XML # # # # #
# # # After XML::Simple # 'NetworkInterface' => { # 'attributes' => { # 'address' => {}, # 'is-home' => {} # } # }, # Create an array of keys if we find a hash if (ref($objects->{$key}->{attributes}) eq 'HASH') { foreach my $k (keys(%{$objects->{$key}->{attributes}})) { push(@{$parsed_objects->{$key}->{attributes}}, $k); } } } elsif (defined($objects->{$key}->{ignored_attributes})) { # XML # # # # #
# # # After XML::Simple # 'NetworkInterface' => { # 'ignored_attributes' => { # 'address' => {}, # 'is-home' => {} # } # }, # Create an array of keys if we find a hash if (ref($objects->{$key}->{ignored_attributes}) eq 'HASH') { foreach my $k (keys(%{$objects->{$key}->{ignored_attributes}})) { push(@{$parsed_objects->{$key}->{ignored_attributes}}, $k); } } } else { $parsed_objects->{$key} = $objects->{$key}; } } if ($log->may_debug()) { $log->comment(Dumper($parsed_objects)); } return $parsed_objects; } ## end sub parse_nacl_objects_file =head2 read_user_specification_files Reads an XML formatted user specification files =over =item Synopsis my $nacl_objects = $dsc->read_user_specification_files(files => 'system_test/config/users'); =item Arguments =over =item C<< files => $scalar >> One or more relative or full paths to the file(s) containing users specifications =back =cut sub read_user_specification_files { my $self = shift; my %incoming = validate_with( params => \@_, spec => {files => {type => SCALAR},}, ); my $paramset = NATE::ParamSet->get_global(); my $top_logdir = $paramset->get('TOP_LOGDIR'); my @files; if (defined($incoming{files})) { @files = split(/;/, $incoming{files}); } my $filtered_user_specifications = {}; if (@files > 0) { foreach my $ucf (@files) { $ucf =~ s/TOP_LOGDIR/$top_logdir/; $log->comment("Reading user specification $ucf"); # Use NATE to search for the file and return the full path my $file = find_file($ucf); if (!$file) { die "unable to find $ucf"; } # Read the XML my $xml = new XML::Simple; my $user_specifications = $xml->XMLin($file, forcearray => ['users']); foreach my $key (keys(%{$user_specifications})) { %{$filtered_user_specifications->{$key}} = %{$user_specifications->{$key}}; } } } return $filtered_user_specifications; } ## end sub read_user_specification_files =back =head1 SEE ALSO This is a derived class of L>. Please see the documentation for more details =head1 AUTHOR/MAINTAINER NACL Development (dl-nacl-dev@netapp.com) =cut 1; __END__