mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-30 19:02:27 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			296 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			296 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
| # IBM(c) 2009 EPL license http://www.eclipse.org/legal/epl-v10.html
 | |
| 
 | |
| #This plugin handles 'setupiscsidev' operation for ONTAP platform devices,
 | |
| #such as the IBM N-series storage subsystems.
 | |
| #It requires that the storage enclosure have root's public key as an
 | |
| #authorized key for passwordless
 | |
| package xCAT_plugin::ontap;
 | |
| 
 | |
| #In ONTAP world, the entire controller is a particular target iqn, that shows
 | |
| #different lun numbers depending on client iqn.
 | |
| #iscsi.target, iscsi.server, and iscsi.lun may therefore look identital
 | |
| #for multiple nodes, but mean entirely different things
 | |
| #This plugin will populate the lun and targetname
 | |
| use strict;
 | |
| 
 | |
| BEGIN
 | |
| {
 | |
|     $::XCATROOT = $ENV{'XCATROOT'} ? $ENV{'XCATROOT'} : '/opt/xcat';
 | |
| }
 | |
| 
 | |
| use lib "$::XCATROOT/lib/perl";
 | |
| use warnings "all";
 | |
| use xCAT::Table;
 | |
| require xCAT::Utils;
 | |
| use xCAT::TableUtils;
 | |
| use xCAT::SvrUtils;
 | |
| my $output_handler;
 | |
| my $newiqns;
 | |
| my $domain;
 | |
| my %nodedomains;
 | |
| my $iscsitab;
 | |
| my $nodetypeinfo;
 | |
| 
 | |
| sub handled_commands {
 | |
|     return {
 | |
|         testontap => 'ontap',
 | |
| 
 | |
|         #        setupiscsidev => iscsi:method
 | |
|         #Mayhaps need an iscsiserver table?  iscsi table column has a pretty weak
 | |
|         #association, but on the other hand, iscsi servers aren't necessarily a node
 | |
|         #So two options:
 | |
|         #  Add field to iscsi table to declare the mechanism to setup device:
 | |
|         #       Pros:
 | |
|         #           -No extra 'nodes' required to be defined
 | |
|         #       Cons:
 | |
|         #           -Could be awkward if the enviornment has hybrid iscsi storage providers
 | |
|         #   Add new table
 | |
|         #       Pros: allows the management method of the target to be abstracted from the node
 | |
|         #       cons: requires that every iscsi entity be defined as a node
 | |
| 
 | |
|       }
 | |
| }
 | |
| my %iscsicfg;
 | |
| 
 | |
| sub process_request {
 | |
|     my $request = shift;
 | |
|     $output_handler = shift;
 | |
|     $iscsitab       = xCAT::Table->new('iscsi');
 | |
|     unless ($iscsitab) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "iSCSI configuration lacking from the iscsi table" ], $output_handler);
 | |
|         return;
 | |
|     }
 | |
|     my @nodes = @{ $request->{node} };
 | |
| 
 | |
|     my $nd = xCAT::NetworkUtils->getNodeDomains(\@nodes);
 | |
|     %nodedomains = %{$nd};
 | |
| 
 | |
|     my $nodetype = xCAT::Table->new('nodetype', -create => 0);
 | |
|     unless ($nodetype) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "ONTAP plugin requires nodetype table to be populated" ], $output_handler);
 | |
|         return;
 | |
|     }
 | |
|     $nodetypeinfo = $nodetype->getNodesAttribs(\@nodes, ['os']);
 | |
|     my $iscsitabdata = $iscsitab->getNodesAttribs(\@nodes, [qw/server lun iname file/]);
 | |
|     my $node;
 | |
|     foreach $node (keys %$iscsitabdata) { #Re-layout the data so we can iterate target-wise rather than node wise
 | |
|         $iscsicfg{ $iscsitabdata->{$node}->[0]->{server} }->{$node} = $iscsitabdata->{$node}->[0];
 | |
|     }
 | |
|     my $controller;
 | |
|     foreach $controller (keys %iscsicfg) {    #TODO: we need to forkify this
 | |
|             #Develop serially first, then put fork semantics in place
 | |
|         handle_targets($controller, $request);
 | |
|     }
 | |
| 
 | |
|     #use Data::Dumper;
 | |
| }
 | |
| 
 | |
| sub get_controller_iqn {
 | |
|     my $controller = shift;
 | |
|     my $output     = `ssh $controller iscsi nodename`;
 | |
|     $output =~ s/^[^:]*://;
 | |
|     chomp $output;
 | |
|     $output =~ s/^\s*//;
 | |
|     return $output;
 | |
| }
 | |
| 
 | |
| sub build_lunmap {
 | |
|     my $controller = shift;
 | |
|     my @nodes      = @_;
 | |
|     my $lunmap;
 | |
|     my @groupoutput = `ssh $controller igroup show `;
 | |
|     my @mapoutput   = `ssh $controller lun show -m`;
 | |
|     shift @mapoutput;    #Get rid of header
 | |
|     shift @mapoutput;
 | |
|     my $groupname;
 | |
|     my $tgr;
 | |
|     my $node;
 | |
|     my $iqn;
 | |
|     my @time  = localtime;
 | |
|     my $year  = 1900 + $time[5];
 | |
|     my $month = $time[4] + 1;
 | |
|     my %returns;
 | |
| 
 | |
|     foreach $node (@nodes) {
 | |
|         $tgr = undef;
 | |
|         $iqn = $iscsicfg{$controller}->{$node}->{iname};
 | |
| 
 | |
|         $domain = $nodedomains{$node};
 | |
|         if ($domain) {
 | |
|             $domain = join(".", reverse(split(/\./, $domain)));
 | |
|         } else {
 | |
|             xCAT::SvrUtils::sendmsg([ 1, "Cannot determine domain name for iqn generation" ], $output_handler);
 | |
|             next;    # ????
 | |
|         }
 | |
| 
 | |
|         unless ($iqn) {    #We must control client iqn, ONTAP acls require it
 | |
|             $newiqns->{$node} = sprintf("iqn.%d-%02d.%s:%s-initiator", $year, $month, $domain, $node);
 | |
|             $iqn = $newiqns->{$node};
 | |
|             $iscsicfg{$controller}->{$node}->{iname} = $iqn;
 | |
|         }
 | |
|         foreach (@groupoutput) {
 | |
|             if (/^    ([^ ]+)/) {    #This is a new group
 | |
|                 $tgr = $1;
 | |
|             } elsif (/^        $iqn/) {
 | |
|                 $groupname = $tgr;
 | |
|             }
 | |
|         }
 | |
|         unless ($groupname) {
 | |
|             next;
 | |
|         }
 | |
|         foreach (@mapoutput) {
 | |
|             unless (/iSCSI/) { next; }
 | |
|             my $backing;
 | |
|             my $igr;
 | |
|             my $lunid;
 | |
|             my $method;
 | |
|             ($backing, $igr, $lunid, $method) = split /\s+/, $_, 4;
 | |
|             if ($igr eq $groupname) {
 | |
|                 $returns{$node}->{$backing} = $lunid;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     foreach $node (keys %$newiqns) { #setNodesAttribs won't work since the values are unique per node
 | |
|         $iscsitab->setNodeAttribs($node, { iname => $newiqns->{$node} });
 | |
|     }
 | |
|     return \%returns;
 | |
| }
 | |
| 
 | |
| 
 | |
| sub handle_targets {
 | |
|     my $controller = shift;
 | |
|     my $request    = shift;
 | |
|     my $node;
 | |
|     my $target = get_controller_iqn($controller);
 | |
|     my @nodes  = keys %{ $iscsicfg{$controller} };
 | |
|     my @upnodes;
 | |
|     foreach $node (@nodes) { #though traversing this is marginally more expensive than just setting,
 | |
|          #do this to allow group level definitions to look sane when manually done
 | |
|         if ($iscsicfg{$controller}->{$node}->{target} ne $target) {
 | |
|             push @upnodes, $node;
 | |
|         }
 | |
|     }
 | |
|     $iscsitab->setNodesAttribs(\@upnodes, { target => $target });
 | |
|     my $lunmap = build_lunmap($controller, keys %{ $iscsicfg{$controller} });
 | |
|     foreach $node (@nodes) {
 | |
|         configure_node($controller, $node, $lunmap, $request);
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| sub getUnits {
 | |
|     my $amount  = shift;
 | |
|     my $defunit = shift;
 | |
|     my $divisor = shift;
 | |
|     unless ($divisor) {
 | |
|         $divisor = 1;
 | |
|     }
 | |
|     if ($amount =~ /(\D)$/) {    #If unitless, add unit
 | |
|         $defunit = $1;
 | |
|         chop $amount;
 | |
|     }
 | |
|     if ($defunit =~ /k/i) {
 | |
|         return $amount * 1024 / $divisor;
 | |
|     } elsif ($defunit =~ /m/i) {
 | |
|         return $amount * 1048576 / $divisor;
 | |
|     } elsif ($defunit =~ /g/i) {
 | |
|         return $amount * 1073741824 / $divisor;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| sub create_new_lun {
 | |
| 
 | |
|     #print Dumper(@_);
 | |
|     my $controller = shift;
 | |
|     my $gname      = shift;
 | |
|     my $cfg        = shift;
 | |
|     my %args       = @_;
 | |
|     my $lunsize;
 | |
|     my $mspec;
 | |
|     if ($args{mspec}) {
 | |
|         $mspec = $args{mspec}
 | |
|     } elsif ($args{lsize}) {
 | |
|         $lunsize = $args{lsize};
 | |
|     }
 | |
| 
 | |
|     my %osmap = (
 | |
|         'rh.*'     => 'linux',
 | |
|         'centos.*' => 'linux',
 | |
|         'sles.*'   => 'linux',
 | |
|         'win2k8'   => 'windows_2008',
 | |
|         'win2k3'   => 'windows',
 | |
|         imagex     => 'windows'
 | |
|     );
 | |
|     unless ($nodetypeinfo->{$gname}->[0]->{os}) {
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "nodetype.os must be set for ONTAP plugin to create a lun" ], $output_handler);
 | |
|     }
 | |
|     my $ltype;
 | |
|     my $ost = $nodetypeinfo->{$gname}->[0]->{os};
 | |
|     foreach (keys %osmap) {
 | |
|         if ($ost =~ /$_/) {
 | |
|             $ltype = $osmap{$_};
 | |
|             last;
 | |
|         }
 | |
|     }
 | |
|     my $gtype = $ltype;
 | |
|     $gtype =~ s/_2008//;    #The group types don't include a 2k8 specific type
 | |
| 
 | |
|     my $file  = $cfg->{file};
 | |
|     my $iname = $cfg->{iname};
 | |
| 
 | |
| 
 | |
|     my $output;
 | |
|     unless (($lunsize or $mspec) and $ltype and $file and $gtype) {    #TODO etc
 | |
|         xCAT::SvrUtils::sendmsg([ 1, "Insufficient data" ], $output_handler);
 | |
|     }
 | |
|     if ($lunsize) {
 | |
|         my $size = getUnits($lunsize, 'g', 1048576);
 | |
|         $size .= "m";
 | |
|         $output = `ssh $controller lun create -s $size -t $ltype $file`;
 | |
|     } elsif ($mspec) {
 | |
|         my $mlun;
 | |
|         my $msnap;
 | |
|         ($mlun, $msnap) = split /@/, $mspec, 2;
 | |
|         my $output = `ssh $controller lun clone create $file -b $mlun $msnap`;
 | |
|     }
 | |
| 
 | |
|     $output = `ssh $controller igroup create -i -t $gtype $gname`;
 | |
|     $output = `ssh $controller igroup add $gname $iname`;
 | |
|     $output = `ssh $controller lun map $file $gname`;
 | |
| }
 | |
| 
 | |
| sub configure_node {
 | |
|     my $controller = shift;
 | |
|     my $node       = shift;
 | |
|     my $cfg        = $iscsicfg{$controller}->{$node};
 | |
|     my $lunmap     = shift;
 | |
|     my $request    = shift;
 | |
|     my $lunsize;
 | |
|     my $masterspec;
 | |
|     if ($request->{arg}) {
 | |
|         use Getopt::Long;
 | |
|         @ARGV = @{ $request->{arg} };
 | |
|         GetOptions(
 | |
|             "size|s=i"   => \$lunsize,
 | |
|             "master|m=s" => \$masterspec,
 | |
|         );
 | |
|     }
 | |
|     unless (defined $lunmap->{$node}->{ $cfg->{file} }) {
 | |
|         if ($lunsize) {
 | |
|             create_new_lun($controller, $node, $cfg, lsize => $lunsize);
 | |
|         } elsif ($masterspec) {
 | |
|             create_new_lun($controller, $node, $cfg, mspec => $masterspec);
 | |
|         } else {
 | |
|             die "IMPLEMENT MAKING NEW LUN";
 | |
|         }
 | |
|     }
 | |
|     if ($cfg->{lun} ne $lunmap->{$node}->{ $cfg->{file} }) {
 | |
|         $iscsitab->setNodeAttribs($node, { lun => $lunmap->{$node}->{ $cfg->{file} } });
 | |
|     }
 | |
| }
 | |
| 
 | |
| 1;
 | |
| 
 | |
| 
 |