Source

iscsi-tools / bin / iscsi

Full commit
#!/usr/bin/env ruby

require 'pp'
require 'clamp'

$:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
require 'iscsi'

class CommandLine < Clamp::Command
  subcommand 'list', 'Lists all targets with the corresponding attribtues' do
    def execute
      ctx = ISCSI::Context.new
      ctx.targets.each do |target|
        puts "#{target.name} (#{target.groups.map(&:name).join(', ')})"
        
        target.views.each do |view| 
          lu = view.lu
          puts "LU #{view.lun} #{lu.name} #{lu.data_file}"
        end
      end
    end
  end

  subcommand 'export', 'Exports a device as iSCSI target' do
    parameter 'GROUP',  'Target group name to choose'
    parameter 'DEVICE ...', 'device paths to export'

    option '--iqn', 'IQN', 'iqn to use for the export'
    option '--tpg', 'PORTAL_GROUP', 'target portal group to use', 
      default: 'frontend'
    
    require 'runner'
    def create_target_group name
      runner = Runner.new

      begin
        runner.shell %Q(stmfadm create-tg #{group})
      rescue Runner::CommandFailed => failure
        case failure.stderr
          when /stmfadm: #{group}: already exists/
            warn "Target group #{group} already exists, continuing..."
        else
          raise
        end
      end
    end
    def create_lu path
      runner = Runner.new

      output = begin
        runner.shell %Q(stmfadm create-lu #{path})
      rescue Runner::CommandFailed => failure
        fail "Device not valid or already in use." 
      end
        
      md = output.match /Logical unit created: (?<luid>[0-9A-F]+)/
      fail "Unexpected output from stmfadm for #{path}." unless md
      
      md[:luid]
    end
    def lookup_lu(path)
      runner = Runner.new
      parser = ISCSI::HashParser.new
      
      output = runner.shell %Q(stmfadm list-lu -v)
      lus = parser.parse(output)
      
      lus.each do |lu_attrs| 
        if lu_attrs[:data_file] == path
          return lu_attrs[:lu_name]
        end
      end
      
      return nil
    end
    def lu_for_path path
      lookup_lu(path) || create_lu(path)
    end
    def add_tg_member group, iqn
      runner = Runner.new

      runner.shell %Q(stmfadm add-tg-member -g #{group} #{iqn})
    rescue Runner::CommandFailed => failure
      case failure.stderr
        when /stmfadm: #{Regexp.escape(iqn)}: already exists/
          warn "IQN #{iqn} is already member of target group #{group}."
      else
        fail "Could not create target group member: #{failure.stderr}"
      end
    end
    def add_view group, path
      runner = Runner.new
      
      luid = lu_for_path(path)
      runner.shell %Q(stmfadm add-view -t #{group} #{luid})
    rescue Runner::CommandFailed => failure
      case failure.stderr
        when /stmfadm: view entry exists/
          warn "LU view exists for #{path}."
      else
        fail "Could not create target group member: #{failure.stderr}"
      end
    end
    
    def execute
      runner = Runner.new
      fail "Must specify an IQN currently" unless iqn
      
      create_target_group group
      
      device_list.each do |path| 
        add_view group, path
      end
      
      add_tg_member group, iqn
      runner.shell %Q(itadm create-target -n #{iqn} -t #{tpg}')
    end
  end
end
CommandLine.run