Class: Gat::Action::ShellCommand

Inherits:
Base
  • Object
show all
Defined in:
lib/gat/action/shell_command.rb,
lib/gatgets/dar_backup/dar_backup.rb

Instance Attribute Summary collapse

Attributes inherited from Base

#argument, #config, #description, #error, #errors, #exit_level, #flags, #name, #onfail, #onfail_callback, #onfail_message, #operation, #output, #parsed_syntax, #times

Instance Method Summary collapse

Methods inherited from Base

#execute, #execute?, get_current_action

Methods included from Interpreter

#interpreter_parameter, #interpreter_parameters, #interpreter_shell_command

Constructor Details

#initialize(name, config, operation) ⇒ ShellCommand

Returns a new instance of ShellCommand.



13
14
15
16
17
# File 'lib/gat/action/shell_command.rb', line 13

def initialize(name, config, operation)
	super(name, config, operation)

 @syntax = config['syntax'] || raise(GatgetConfigException.new("Shell Command Action #{ name } has not a syntax attribute", "initialize_shell_command"))
end

Instance Attribute Details

#argumentsObject

Returns the value of attribute arguments.



8
9
10
# File 'lib/gat/action/shell_command.rb', line 8

def arguments
  @arguments
end

#childsObject (readonly)

Returns the value of attribute childs.



7
8
9
# File 'lib/gat/action/shell_command.rb', line 7

def childs
  @childs
end

#pidObject

Returns the value of attribute pid.



10
11
12
# File 'lib/gat/action/shell_command.rb', line 10

def pid
  @pid
end

#statusObject

Returns the value of attribute status.



11
12
13
# File 'lib/gat/action/shell_command.rb', line 11

def status
  @status
end

#syntaxObject (readonly)

Returns the value of attribute syntax.



6
7
8
# File 'lib/gat/action/shell_command.rb', line 6

def syntax
  @syntax
end

Instance Method Details

#exec(shell_command, multiple_name = '') ⇒ Object

Execute shell command with Open4

Open4 module is the next generation Ruby module for executing shell commands. Open4 works better thant the standard Open3 module, and has built inside the feature to return exit status through Process.status Class. Even it has some problems too. Open4 module has big problems when the stdout or the stderr comes with tons of information. For example, with a mysqldump output.

The solution presented here is a wrapper through Process.fork. We create a new fork, making and the output is not managed by the ruby script itself but by the fork process. IO.pipes are created to pass the stdout, stderr and stdin data through Gat proccess and Fork process

Some disccusion about this can be readed here: stackoverflow.com/questions/1076257/returning-data-from-forked-processes

Maybe, we could use Theread.new, and maybe it would be better, but… =)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/gat/action/shell_command.rb', line 64

def exec(shell_command, multiple_name = '')

  trap_save = trap( 0, "IGNORE" )

  self.operation.gatget.logger.log("trace", "action_syntax", "Action #{ self.name }#{ multiple_name } exec command syntax : #{ shell_command }" )

 self.times['init'] ||= Time.now

 read_stdout, write_stdout = IO.pipe
 read_stderr, write_stderr = IO.pipe

 pid = fork do
    trap( 0, "IGNORE" )
   read_stdout.close
   read_stderr.close

   pid_open4, stdin, stdout, stderr = Open4::popen4 "#{ shell_command }"

   write_stdout.puts stdout.read
   write_stderr.puts stderr.read

   ignored, status_open4 = Process::waitpid2 pid_open4
   exit status_open4.exitstatus
 end
  trap( 0, trap_save )

 write_stdout.close
 write_stderr.close

 result_stdout = read_stdout.read
 result_stderr = read_stderr.read

 pid, status = Process.wait2(pid)

 self.output, self.error, self.exit_level = reinterpret_output_and_error(result_stdout, result_stderr, status.exitstatus)

 self.pid = pid
  self.status = status
 self.times['end'] = Time.now
end

#reinterpret_output_and_error(output, error, status) ⇒ Object

dar mixes a little bit its stdout and stderr outputs



145
146
147
148
# File 'lib/gat/action/shell_command.rb', line 145

def reinterpret_output_and_error(output, error, status)
 return output == "\n" ? "" : output,
       error == "\n" ? "" : error, status
end

#runObject



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/gat/action/shell_command.rb', line 20

def run
  ret = nil
  # Shell Command can be multiple, that means the command will be execute over a foreach variable subtitutions.
  # That is usefull, for example, when you need to do the same actions with some variable subtition, like
  # a multiple files compression.
  @childs = (@config['multiple'] and @config['multiple'].any?) ? set_multiple_childs(@config['multiple']) : [ ]

  # If ShellCommand is multiple, then, we exec all of its childs
  # If not, we just execute ShellCommand instance
  if @childs.any?
    @childs.each { |child|
      if not child.exec
        ret = false
      end
    }
	else
	  self.parsed_syntax = interpreter_parameters(self.syntax, self.operation.gatget)

	  ret = exec(self.parsed_syntax)
	end

	if self.config['output']
	  self.operation.gatget.logger.log("direct", "action_output", self.output)
  end
  ret
end

#set_multiple_childs(multiples_parameters) ⇒ Object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/gat/action/shell_command.rb', line 106

def set_multiple_childs(multiples_parameters)
	      # get multiples values
	      multiples_values = {}
	      multiples_parameters.each do |parameter|
 values = interpreter_parameter(parameter, self.operation.gatget)
 multiples_values[parameter] = values
	      end

	      # all multiple values must have the same size for substitution
	      last_multiple_vales_size = multiples_values.values.first.size
	      multiples_values.values.each do |values|
 unless values.size == last_multiple_vales_size
   raise GatgetConfigException.new("Multiples values at Action #{ self.name } doesnt have the same size. Substitution cannot be interpreted", "set_multiple_chids")
 end
 last_multiple_vales_size == values.size
  end


 unless last_multiple_vales_size > 0
   raise GatgetConfigException.new("Multiples values at Action #{ self.name } are empty", "set_multiple_chids")
 end

 # create childs objects
 childs = []
 i = 0
 last_multiple_vales_size.times do
   child_multiple_parameters = {}
   multiples_values.each_pair do |parameter, values|
     child_multiple_parameters[parameter] = values[i]
   end
   childs << ShellCommandChild.new(self, child_multiple_parameters)
   i += 1
 end

 # return child
 childs
end