Class: Proxy::RemoteExecution::Ssh::Session

Inherits:
Dynflow::Actor
  • Object
show all
Defined in:
lib/smart_proxy_remote_execution_ssh_core/session.rb

Overview

Service that handles running external commands for Actions::Command Dynflow action. It runs just one (actor) thread for all the commands running in the system and updates the Dynflow actions periodically.

Constant Summary collapse

EXPECTED_POWER_ACTION_MESSAGES =
["restart host", "shutdown host"]

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Session

Returns a new instance of Session.



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/smart_proxy_remote_execution_ssh_core/session.rb', line 10

def initialize(options = {})
  @clock = options[:clock] || Dynflow::Clock.spawn('proxy-dispatcher-clock')
  @logger = options[:logger] || Logger.new($stderr)
  @connector_class = options[:connector_class] || Connector
  @local_working_dir = options[:local_working_dir] || Settings.instance.local_working_dir
  @remote_working_dir = options[:remote_working_dir] || Settings.instance.remote_working_dir
  @refresh_interval = options[:refresh_interval] || 1
  @client_private_key_file = Settings.instance.ssh_identity_key_file
  @command = options[:command]

  @command_buffer = []
  @refresh_planned = false

  reference.tell(:initialize_command)
end

Instance Method Details

#dispatcherObject



105
106
107
# File 'lib/smart_proxy_remote_execution_ssh_core/session.rb', line 105

def dispatcher
  self.parent
end

#finish_commandObject



100
101
102
103
# File 'lib/smart_proxy_remote_execution_ssh_core/session.rb', line 100

def finish_command
  close
  dispatcher.tell([:finish_command, @command])
end

#initialize_commandObject



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/smart_proxy_remote_execution_ssh_core/session.rb', line 26

def initialize_command
  @logger.debug("initalizing command [#{@command}]")
  open_connector
  remote_script = cp_script_to_remote
  output_path = File.join(File.dirname(remote_script), 'output')

  # pipe the output to tee while capturing the exit code
  script = <<-SCRIPT
    exec 4>&1
    exit_code=`((#{su_prefix}#{remote_script}; echo $?>&3 ) | /usr/bin/tee #{output_path} ) 3>&1 >&4`
    exec 4>&-
    exit $exit_code
  SCRIPT
  @logger.debug("executing script:\n#{script.lines.map { |line| "  | #{line}" }.join}")
  @connector.async_run(script) do |data|
    @command_buffer << data
  end
rescue => e
  @logger.error("error while initalizing command #{e.class} #{e.message}:\n #{e.backtrace.join("\n")}")
  @command_buffer.concat(CommandUpdate.encode_exception("Error initializing command #{@command}", e))
  refresh
ensure
  plan_next_refresh
end

#killObject



88
89
90
91
92
93
94
95
96
97
98
# File 'lib/smart_proxy_remote_execution_ssh_core/session.rb', line 88

def kill
  @logger.debug("killing command [#{@command}]")
  if @connector
    @connector.run("pkill -f #{remote_command_file('script')}")
  else
    @logger.debug("connection closed")
  end
rescue => e
  @command_buffer.concat(CommandUpdate.encode_exception("Failed to kill the command", e, false))
  plan_next_refresh
end

#refreshObject



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/smart_proxy_remote_execution_ssh_core/session.rb', line 51

def refresh
  @connector.refresh if @connector

  unless @command_buffer.empty?
    status = refresh_command_buffer
    if status
      finish_command
    end
  end
rescue Net::SSH::Disconnect => e
  check_expecting_disconnect
  if @expecting_disconnect
    @command_buffer << CommandUpdate::StatusData.new(0)
  else
    @command_buffer.concat(CommandUpdate.encode_exception("Failed to refresh the connector", e, true))
  end
  refresh_command_buffer
  finish_command
rescue => e
  @command_buffer.concat(CommandUpdate.encode_exception("Failed to refresh the connector", e, false))
ensure
  @refresh_planned = false
  plan_next_refresh
end

#refresh_command_bufferObject



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/smart_proxy_remote_execution_ssh_core/session.rb', line 76

def refresh_command_buffer
  @logger.debug("command #{@command} got new output: #{@command_buffer.inspect}")
  command_update = CommandUpdate.new(@command_buffer)
  check_expecting_disconnect
  @command.suspended_action << command_update
  @command_buffer = []
  if command_update.exit_status
    @logger.debug("command [#{@command}] finished with status #{command_update.exit_status}")
    return command_update.exit_status
  end
end

#start_termination(*args) ⇒ Object



109
110
111
112
113
# File 'lib/smart_proxy_remote_execution_ssh_core/session.rb', line 109

def start_termination(*args)
  super
  close
  finish_termination
end