# # Copyright (c) 2001-2012 NetApp, Inc., All Rights Reserved # Any use, modification, or distribution is prohibited # without prior written consent from NetApp, Inc. # ## @summary IPv6 Task Module ## @author dl-nacl-dev@netapp.com ## @status shared ## @pod here package NACL::MTask::IPv6; use base qw(NACL::MTask::MTask); 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 NACL::STask::NetworkInterface; use NACL::STask::NetworkRoutingGroupsRoute; use NACL::STask::NetworkRoute; use NACL::C::NetworkOptionsIpv6; use NACL::C::Options; use NACL::CS::Options; use NACL::ComponentUtils qw(Dumper); use Net::IP qw(:PROC); use NATE::Exceptions::Argument qw(); use NATE::BaseException qw(:try); use Params::Validate qw(validate validate_with BOOLEAN SCALAR OBJECT ARRAYREF); =head1 NAME NACL::MTask::IPv6 =head1 DESCRIPTION C provides a number of well-defined but potentially complex or multi-step methods related to IPv6 setup in ONTAP. This task is applicable for both CMode and 7Mode. =head1 ATTRIBUTES =head2 command_interface (Required) A component object that represents the host to which to send commands. =head1 EXCEPTIONS 1. NATE::Exceptions::Argument (If correct gateway is not specified, this exception will be thrown) 2. NATE::BaseException (If ONTAP build doesn't support IPv6, this exception will be thrown) =head1 METHODS =head2 ipv6_setup For CMode : =========== NACL::MTask::IPv6->ipv6_setup( command_interface => $ci, interfaces => [ { lif => $lif_name1, vserver => $vserver_name1, role => $role1, 'home-node' => $homenode1, 'home-port' => $homeport1, address => $address1, 'netmask-length' => $string1, ... }, { lif => $lif_name2, vserver => $vserver_name2, role => $role2, 'home-node' => $homenode2, 'home-port' => $homeport2, address => $address2, 'netmask-length' => $string2, ... }, .... ], mgmt_gateway => $mgmt_gateway, data_gateway => $data_gateway, cluster_gateway => $cluster_gateway, intercluster_gateway => $intercluster_gateway, nacltask_if_failed => 'cleanup' # default is die ... ); For 7Mode : =========== NACL::MTask::IPv6->ipv6_setup( command_interface => $ci, nacltask_if_failed => 'cleanup' # default is die ); (Class Method) This function takes interfaces, mgmt_gateway, data_gateway, cluster_gateway and intercluster_gateway as parameters and performs IPv6 setup for CMode filer. IPv6 setup steps for CMode are: 1. Enable IPv6. 2. Create and verify Ipv6 lifs. 3. Add Default route for all routing groups and verify default routes. For 7mode, below options need to be enabled. 1. cifs.ipv6.enable on 2. ftpd.ipv6.enable on 3. httpd.ipv6.enable on 4. ip.v6.enable on 5. ip.v6.ra_enable on 6. nfs.ipv6.enable on In case of failure, exception will be thrown. =over =item Options =over =item C<< command_interface => $ci >> (Required) A component object that represents the host to which to send commands. See L =item C<< interfaces => \@interfaces >> (Required) Reference of an array of hashes containing parameters for lif creation. Lif creation will be done in the order specified in the array. Applicable only for CMode, not required for 7Mode. Required attributes for lif creation are lif (Required) : Lif name vserver (Required) : Vserver name role (Required) : Lif role (ex: data, node-mgmt, etc) home-node (Required) : Home node name home-port (Required) : Home port (ex: e0c, e0d etc) address (Required) : IPv6 address (ex: fd20:8b1e:b255:814e:0000:0000:0100:0042) netmask-length (Required) : Netmask length (ex: 64) =item C<< mgmt_gateway => $mgmt_gateway >> (Optional, Required if interface specified is of type node mgmt) Node management gateway. Applicable only for CMode, not required for 7Mode. =item C<< data_gateway => $data_gateway >> (Optional, Required if interface specified is of type data) Data gateway. Applicable only for CMode, not required for 7Mode. =item C<< nacltask_route_create_verify => $boolean >> (Optional, default: 1) This option enables/disables verificatoin of route creation. =item C<< cluster_gateway => $cluster_gateway >> (Optional, Required if interface specified is of type cluster mgmt) Cluster management gateway. Applicable only for CMode, not required for 7Mode. =item C<< intercluster_gateway => $intercluster_gateway >> (Optional, Required if interface specified is of type intercluster) Inter Cluster gateway. Applicable only for CMode, not required for 7Mode. =item C<< destination => $destination >> (Optional, default is '::/0') The destination represented as IpAddress/Mask. Applicable only for CMode, not required for 7Mode. =item C<< nacltask_if_failed => $action >> Specifies an action to be taken if failed to setup IPv6. Applicable for both CMode and 7Mode. $action - if set to "die" (default), re-throw the exception occured during setup. - if set to "pass", warning msg will be logged. - if set to "cleanup", partial changes will be reverted. =back =back =cut sub ipv6_setup { $Log->enter() if $may_enter; my $pkg = shift; my %opts = validate_with( params => \@_, spec => { command_interface => { isa => 'NACL::C::CommandInterface::ONTAP' }, nacltask_if_failed => { type => SCALAR, optional => 1, callbacks => { "Specify valid value for the option nacltask_if_failed" . " ('die', 'pass' or 'cleanup')" => sub { $_[0] =~ m[^(die)|(pass)|(cleanup)$]; } } }, nacltask_route_create_verify => { type => SCALAR, default => 1}, }, allow_extra => 1, ); my $ci = $opts{command_interface}; $ci->mode eq 'CMode' ? $pkg->_cmode_ipv6_setup(%opts) : $pkg->_7mode_ipv6_setup(%opts); $Log->exit() if $may_exit; } ## end sub ipv6_setup sub _7mode_ipv6_setup { $Log->enter() if $may_enter; my ( $pkg, %opts ) = @_; my %options = ( 'cifs.ipv6.enable' => 'on', 'ftpd.ipv6.enable' => 'on', 'httpd.ipv6.enable' => 'on', 'ip.v6.enable' => 'on', 'ip.v6.ra_enable' => 'on', 'nfs.ipv6.enable' => 'on' ); my %cleanup; foreach my $option ( keys %options ) { try { my $cs = NACL::CS::Options->fetch( command_interface => $opts{command_interface}, filter => { name => $option } ); $cleanup{$option} = $cs->name; NACL::C::Options->option( command_interface => $opts{command_interface}, 'option-name' => $option, 'option-value' => $options{$option} ); } catch NATE::BaseException with { my $e = shift; my $e_text = $e->text(); if ( defined $opts{nacltask_if_failed} && $opts{nacltask_if_failed} eq 'cleanup' ) { foreach my $key ( keys %cleanup ) { NACL::C::Options->option( command_interface => $opts{command_interface}, 'option-name' => $key, 'option-value' => $options{$key} ); } goto EXIT; } elsif ( $opts{nacltask_if_failed} eq 'pass' ) { $Log->warn( "IPv6 setup failed with exception, $e_text. " . "Since $opts{nacltask_if_failed} option is " . "provided for nacltask_if_failed, no cleanup will" . " be done" ); } else { $Log->exit() if $may_exit; $e->throw(); } }; } EXIT: $Log->exit() if $may_exit; } ## end sub _7mode_ipv6_setup sub _cmode_ipv6_setup { $Log->enter() if $may_enter; my $pkg = shift; my %opts = validate_with( params => \@_, spec => { command_interface => { isa => 'NACL::C::CommandInterface::ONTAP' }, interfaces => { type => ARRAYREF }, destination => { type => SCALAR, default => '::/0' }, mgmt_gateway => { type => SCALAR, optional => 1, callbacks => { "Specify valid IPv6 address for mgmt_gateway" => sub { return Net::IP::ip_is_ipv6( $_[0] ); } } }, data_gateway => { type => SCALAR, optional => 1, callbacks => { "Specify valid IPv6 address for data_gateway" => sub { return Net::IP::ip_is_ipv6( $_[0] ); } } }, cluster_gateway => { type => SCALAR, optional => 1, callbacks => { "Specify valid IPv6 address for cluster_gateway" => sub { return Net::IP::ip_is_ipv6( $_[0] ); } } }, intercluster_gateway => { type => SCALAR, optional => 1, callbacks => { "Specify valid IPv6 address for intercluster_gateway" => sub { return Net::IP::ip_is_ipv6( $_[0] ); } } }, }, allow_extra => 1 ); my $ci = $opts{command_interface}; my $destination = $opts{destination}; my $nacltask_route_create_verify = delete $opts{'nacltask_route_create_verify'}; # Verify gateway foreach my $lif ( @{ $opts{interfaces} } ) { if ( $lif->{role} eq 'data' && !defined $opts{data_gateway} ) { $Log->exit() if $may_exit; NATE::Exceptions::Argument->throw( 'data_gateway is required ' . 'param if the interface is of type data' ); } elsif ( $lif->{role} eq 'cluster-mgmt' && !defined $opts{cluster_gateway} ) { $Log->exit() if $may_exit; NATE::Exceptions::Argument->throw( 'cluster_gateway is required ' . 'param if the interface is of type cluster-mgmt' ); } elsif ( $lif->{role} eq 'node-mgmt' && !defined $opts{mgmt_gateway} ) { $Log->exit() if $may_exit; NATE::Exceptions::Argument->throw( 'mgmt_gateway is required ' . 'param if the interface is of type node-mgmt' ); } elsif ( $lif->{role} eq 'intercluster' && !defined $opts{intercluster_gateway} ) { $Log->exit() if $may_exit; NATE::Exceptions::Argument->throw( 'intercluster_gateway is required ' . 'parameter, if the interface is of type intercluster' ); } } # Check if IPv6 is supported in the ontap version. my $is_ipv6_supported = $ci->is_feature_supported( feature => 'ipv6_feature' ); if ( !$is_ipv6_supported ) { $Log->exit() if $may_exit; NATE::BaseException->throw( 'IPv6 is not supported in this ONTAP version'); } # Enable IPv6. Once enabled, IPv6 support cannot be # disabled, so no cleanup will be done for this change NACL::C::NetworkOptionsIpv6->modify( command_interface => $ci, enabled => 'true' ); my @cleanup; # Validate interfaces params foreach my $lif ( @{ $opts{interfaces} } ) { my %args = validate_with( params => $lif, spec => { NACL::STask::NetworkInterface->_primary_keys_validate_spec( command_interface => $ci ) }, allow_extra => 1 ); # Create IPv6 interfaces and add Default # route for all routing groups try { my $was_created = 0; my $stask = NACL::STask::NetworkInterface->create( command_interface => $ci, nacltask_if_exists => 'reuse', nacltask_verify => 1, _was_created => \$was_created, %args, ); push @cleanup, [ $stask, $was_created ]; my $interface_cs = $stask->state; my $role = $interface_cs->role; my $gateway; if ( $role eq 'data' ) { $gateway = $opts{data_gateway}; } elsif ( $role eq 'node-mgmt' ) { $gateway = $opts{mgmt_gateway}; } elsif ( $role eq 'cluster-mgmt' ) { $gateway = $opts{cluster_gateway}; } elsif ( $role eq 'intercluster' ) { $gateway = $opts{intercluster_gateway}; } $was_created = 0; my %args_to_create_route = (command_interface => $ci, vserver => $args{vserver}, destination => $destination, gateway => $gateway, nacltask_if_exists => 'reuse', _was_created => \$was_created, nacltask_verify => $nacltask_route_create_verify, ); my $pkg_name; if ($ci->has_uichange(uichange => 'ipspaces-ms4')){ $pkg_name = 'NACL::STask::NetworkRoute'; } else { $pkg_name = 'NACL::STask::NetworkRoutingGroupsRoute'; $args_to_create_route{'routing-group'} = $interface_cs->routing_group; } my $route = $pkg_name->create( %args_to_create_route ); push @cleanup, [ $route, $was_created ]; } catch NATE::BaseException with { my $e = shift; my $e_text = $e->text(); $Log->debug( 'Exception : ' . $e_text ); if ( $opts{nacltask_if_failed} eq 'cleanup' ) { $Log->debug( 'nacltask_if_failed : ' . $opts{nacltask_if_failed} ); foreach my $array (@cleanup) { if ( $array->[1] ) { $array->[0]->purge( nacltask_verify => 1, nacltask_if_purged => 'pass' ); } } goto EXIT; } elsif ( $opts{nacltask_if_failed} eq 'pass' ) { $Log->warn( "IPv6 setup failed with exception, $e_text. " . "Since $opts{nacltask_if_failed} option is " . "provided for nacltask_if_failed, no cleanup will" . " be done" ); } else { $Log->exit() if $may_exit; $e->throw(); } }; } EXIT: $Log->exit() if $may_exit; } ## end sub _cmode_ipv6_setup 1;