#!/usr/bin/env ruby # -*- coding: utf-8 -*- begin require 'rubygems' require 'bundler' Bundler.setup(:default) rescue Exception end require File.expand_path('../../config/path_resolver', __FILE__) include Isono::Runner::RpcServer class ZfsHandler < EndpointBuilder include Dcmgr::Logger job :create_volume do volume_id = request.args[0] job = Dcmgr::Stm::VolumeContext.new(volume_id) data = rpc.request('sta-collector', 'get_volume', volume_id) raise "Invalid volume state: #{data[:state]}" unless data[:state].to_s == 'registering' unless data[:snapshot_id].nil? sdata = rpc.request('sta-collector', 'get_snapshot', data[:snapshot_id]) raise "Invalid snapshot state: #{sdata[:state]}" unless sdata[:state].to_s == 'available' end logger.info("creating new volume #{volume_id}") job.stm.state=data[:state].to_sym job.stm.on_create vol_path = "#{data[:storage_pool][:export_path]}/#{data[:export_path]}" `zfs list #{File.dirname(vol_path)} > /dev/null 2>&1` if $?.exitstatus != 0 # create parent filesystem `zfs create -p #{File.dirname(vol_path)}` logger.info("create parent filesystem: #{File.dirname(vol_path)}") end if sdata # create volume from snapshot v = `zfs receive #{vol_path} < #{data[:storage_pool][:snapshot_base_path]}/#{sdata[:account_id]}/#{sdata[:uuid]}.zsnap` if $?.exitstatus != 0 raise "volume already exists: #{volume_id}" end v = `zfs destroy #{vol_path}@#{sdata[:uuid]}` if $?.exitstatus != 0 raise "volume snapshot has not deleted: #{volume_id}@#{sdata[:uuid]}" end vl = `zfs list #{vol_path}` if vl.nil? || $?.exitstatus != 0 raise "volume has not be created: #{volume_id}" end else # create volume v = `zfs create -p -V #{data[:size]}m #{vol_path}` if $?.exitstatus != 0 raise "volume already exists: #{volume_id}" end vl = `zfs list #{vol_path}` if vl.nil? || $?.exitstatus != 0 raise "volume has not be created: #{volume_id}" end end rpc.request('sta-collector', 'update_volume', job.to_hash(:export_path=>data[:export_path])) logger.info("created new volume: #{volume_id}") job.stm.on_register vr = `zfs shareiscsi=on #{data[:storage_pool][:export_path]}/#{data[:uuid]}` if $?.exitstatus != 0 raise "failed iscsi target request: #{volume_id}" end il = `iscsitadm list target -v #{data[:storage_pool][:export_path]}/#{data[:uuid]}` if $?.exitstatus != 0 raise "iscsi target has not be created #{volume_id}" end il = il.downcase.split("\n").select {|row| row.strip!} # :transport_information => {:iqn => "iqn.1986-03.com.sun:02:787bca42-9639-44e4-f115-f5b06ed31817", :lun => 0} opt = {:iqn => il[0].split(": ").last, :lun=>il[6].split(": ").last.to_i} rpc.request('sta-collector', 'update_volume', job.to_hash(:transport_information=>opt)) logger.info("registered iscsi target: #{volume_id}") end job :delete_volume do volume_id = request.args[0] job = Dcmgr::Stm::VolumeContext.new(volume_id) data = rpc.request('sta-collector', 'get_volume', volume_id) logger.info("deleting volume: #{volume_id}") raise "Invalid volume state: #{data[:state]}" unless data[:state].to_s == 'deregistering' job.stm.state=data[:state].to_sym # deregisterd iscsi target job.stm.on_delete vr = `zfs shareiscsi=off #{data[:storage_pool][:export_path]}/#{data[:export_path]}` il = `iscsitadm list target #{data[:storage_pool][:export_path]}/#{data[:export_path]}` unless il.empty? raise "iscsi target has not deleted: #{volume_id} iqn: #{data[:transport_information][:iqn]}" end rpc.request('sta-collector', 'update_volume', job.to_hash) logger.info("deregistered iscsi target: #{volume_id} iqn: #{data[:transport_information][:iqn]}") # delete volume job.stm.on_delete job.on_delete v = `zfs destroy #{data[:storage_pool][:export_path]}/#{data[:export_path]}` vl = `zfs list #{data[:storage_pool][:export_path]}/#{data[:export_path]}` unless vl.empty? raise "volume has not deleted: #{volume_id}" end rpc.request('sta-collector', 'update_volume', job.to_hash) logger.info("deleted volume: #{volume_id}") end job :create_snapshot do snapshot_id = request.args[0] job = Dcmgr::Stm::SnapshotContext.new(snapshot_id) sdata = rpc.request('sta-collector', 'get_snapshot', snapshot_id) unless snapshot_id.nil? data = rpc.request('sta-collector', 'get_volume', sdata[:origin_volume_id]) logger.info("create new snapshot: #{snapshot_id}") raise "Invalid volume state: #{data[:state]}" unless data[:state].to_s == 'available' job.stm.state=sdata[:state].to_sym job.stm.on_create vol_path = "#{data[:storage_pool][:export_path]}/#{data[:export_path]}" snap_dir = "#{data[:storage_pool][:snapshot_base_path]}/#{sdata[:account_id]}" unless File.exist?(snap_dir) # create a directory to save snapshot `mkdir -p #{snap_dir}` logger.info("create a directory: #{snap_dir}") end snap = `zfs snapshot #{vol_path}@#{sdata[:uuid]}` rpc.request('sta-collector', 'update_snapshot', job.to_hash) logger.info("creating new snapshot: #{snapshot_id}") job.stm.on_create snap_send = `zfs send #{vol_path}@#{sdata[:uuid]} > #{snap_dir}/#{sdata[:uuid]}.zsnap` snap_delete = `zfs destroy #{vol_path}@#{sdata[:uuid]}` rpc.request('sta-collector', 'update_snapshot', job.to_hash) logger.info("created new snapshot: #{snapshot_id}") end job :delete_snapshot do snapshot_id = request.args[0] job = Dcmgr::Stm::SnapshotContext.new(snapshot_id) sdata = rpc.request('sta-collector', 'get_snapshot', snapshot_id) data = rpc.request('sta-collector', 'get_volume', sdata[:origin_volume_id]) logger.info("deleting snapshot: #{snapshot_id}") raise "Invalid snapshot state: #{sdata[:state]}" unless sdata[:state].to_s == 'deleting' job.stm.state=sdata[:state].to_sym job.stm.on_delete system("rm -rf #{data[:storage_pool][:snapshot_base_path]}/#{sdata[:account_id]}/#{sdata[:uuid]}.zsnap") raise "snapshot has not be deleted" if $?.exitstatus != 0 rpc.request('sta-collector', 'update_snapshot', job.to_hash) logger.info("deleted snapshot: #{snapshot_id}") end def rpc @rpc ||= Isono::NodeModules::RpcChannel.new(@node) end def jobreq @jobreq ||= Isono::NodeModules::JobChannel.new(@node) end def event @event ||= Isono::NodeModules::EventChannel.new(@node) end end manifest = Isono::Runner::RpcServer::DEFAULT_MANIFEST.dup manifest.instance_eval do node_name 'sta' node_instance_id "#{Isono::Util.default_gw_ipaddr}" load_module Isono::NodeModules::NodeHeartbeat end start(manifest) do endpoint "zfs-handle.#{@node.node_id}", ZfsHandler end