Hemdal.Host behaviour (Hemdal v1.0.3)
View SourceHost is spawning processes to run tasks for the corresponding host. It's a way of limiting the amount of commands triggered against a host and ensure the rest of the commands stay in the queue.
It's also the base for the implementation of the way of running commands. At the moment the available way to run commands are:
Hemdal.Host.Local
intended to run local commands.Hemdal.Host.Trooper
intended to run SSH commands. It's not included in the main repository, you can find more information here.
If you want to know more about what could be included in a command or to be run in a host you can review the following modules:
Hemdal.Config.Alert.Command
where you can check what's included inside of the command.Hemdal.Config.Host
where you can find information about the host.
Implement a new Host
If you need to implement a new way to run external commands, you can
create a new Hemdal.Host.XXX
module which will be using the module
Hemdal.Host
inside. For example, if you need to implement a telnet
way to access to the remote hosts, you can implement a module as follows:
defmodule Hemdal.Host.Telnet do
use Hemdal.Host
@impl Hemdal.Host
def exec(host, command) do
# do your stuff
{:ok, errorlevel, output}
end
@impl Hemdal.Host
def write_file(host, filename, content) do
raise "Impossible to transfer files"
end
@impl Hemdal.Host
def delete(host, filename) do
raise "Impossible to remove files"
end
end
While telnet isn't prepared to transfer files, it's raising an error which is telling the system that's impossible to use the script way for running commands.
Summary
Types
The command to be executed.
The command arguments to be passed with the execution command.
The errorlevel is shell concept. In the shell every command is returning an integer as the errorlevel after the running, if that's 0 (zero), it's meaning the execution was fine, otherwise a positive or negative number means the running was wrong and the number could means something different depending on the command.
Handler is used by the backend implementation of the host, it could be whatever depending on the needs of the backend implementation. For example, it could be the SSH connection or the credentials, or the way to access to the PTY or TTY. See the implementations for further details.
The console output. It will be decoded using JSON to determine what's the information processed and generated by the command to be processed.
The reason of the failure. It's usually an atom, but it could be whatever.
Callbacks
Remove a file which was created with write_file/3
when the execution of
the script was finalised.
Exec a command using the method implemented by the module where it's
implemented. The exec/2
command is getting a handler from the transaction
and the command to be executed as a string.
The transaction is an optional callback which is implemented by default if
you are using use Hemdal.Host
and it's ensuring you can provide to the
write_file/3
, exec/2
and delete/2
the same handler which must be
provided to the running function. As an example, the default implementation
of this callback is as follows
Write a file in the remote (or local) host. It's intended to write the
scripts which will be needed to be executed after that with exec/2
.
Functions
Returns a specification to start this module under a supervisor.
Run or execute the command passed as parameter. It's needed to pass the host ID to find the process where to send the request, and the the command and the arguments to run the command.
Check if the host is started based on the host ID passed as parameter.
Get all the host IDs that are running at the moment of calling this function.
Retrieve the PID providing the host ID.
Performs a reload for all of the hosts. It's retrieving the configuration for
all of the hosts and applying it for each host. It's reloading the
configuration for each host and applying it through the update_host/1
function.
Start a new host under the Hemdal.Host.Supervisor
module.
Start all of the hosts based on the configuration. It's retrieving all of
the hosts from the configuration and the using each host with the start/1
function.
Start a new host directly. It's intended to be in use from the supervisor, but it could be used for test purposes.
Terminate the processes related to a process ID.
Update the host configuration. If the host isn't running it's starting it
and passing it the configuration provided as the host
parameter. In a
nutshell, it's: if exist host ID then perform an update of the host
information, else start the process with the host information.
Types
@type command() :: String.t()
The command to be executed.
@type command_args() :: [String.t()]
The command arguments to be passed with the execution command.
@type errorlevel() :: integer()
The errorlevel is shell concept. In the shell every command is returning an integer as the errorlevel after the running, if that's 0 (zero), it's meaning the execution was fine, otherwise a positive or negative number means the running was wrong and the number could means something different depending on the command.
@type handler() :: any()
Handler is used by the backend implementation of the host, it could be whatever depending on the needs of the backend implementation. For example, it could be the SSH connection or the credentials, or the way to access to the PTY or TTY. See the implementations for further details.
@type output() :: String.t()
The console output. It will be decoded using JSON to determine what's the information processed and generated by the command to be processed.
@type reason() :: any()
The reason of the failure. It's usually an atom, but it could be whatever.
@type t() :: %Hemdal.Host{ host: nil | Hemdal.Config.Host.t(), max_workers: :infinity | non_neg_integer(), queue: :queue.queue({GenServer.from(), command(), command_args()}), workers: non_neg_integer() }
Callbacks
Remove a file which was created with write_file/3
when the execution of
the script was finalised.
@callback exec(handler(), command()) :: {:ok, errorlevel(), output()} | {:error, reason()}
Exec a command using the method implemented by the module where it's
implemented. The exec/2
command is getting a handler from the transaction
and the command to be executed as a string.
@callback transaction(Hemdal.Config.Host.t(), (handler() -> any())) :: any()
The transaction is an optional callback which is implemented by default if
you are using use Hemdal.Host
and it's ensuring you can provide to the
write_file/3
, exec/2
and delete/2
the same handler which must be
provided to the running function. As an example, the default implementation
of this callback is as follows:
@impl Hemdal.Host
def transaction(host, f), do: f.(host)
@callback write_file(handler(), tmp_file :: String.t(), content :: String.t()) :: :ok | {:error, reason()}
Write a file in the remote (or local) host. It's intended to write the
scripts which will be needed to be executed after that with exec/2
.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor
.
@spec exec(host_id(), Hemdal.Config.Alert.Command.t(), command_args()) :: {:ok, map()} | {:error, map()}
Run or execute the command passed as parameter. It's needed to pass the host ID to find the process where to send the request, and the the command and the arguments to run the command.
It's returning a tuple for :ok
or :error
and a set of data which depends on
if it was success or not.
The success return data is usually including the following keys (all of them as strings):
status
which could beOK
,FAIL
,WARN
orUNKNOWN
.message
which is a string containing a message to show.
The failure return data is usually similar to the previous one but it could be
containing something different depending on the return of the remote command.
If the JSON sent back from the command is valid, it's usually using that as
data and it's marked as UNKNOWN
status if there is no status defined.
@spec exists?(host_id()) :: boolean()
Check if the host is started based on the host ID passed as parameter.
@spec get_all() :: [host_id()]
Get all the host IDs that are running at the moment of calling this function.
@spec get_pid(host_id()) :: pid() | nil
Retrieve the PID providing the host ID.
@spec reload_all() :: :ok
Performs a reload for all of the hosts. It's retrieving the configuration for
all of the hosts and applying it for each host. It's reloading the
configuration for each host and applying it through the update_host/1
function.
@spec start(Hemdal.Config.Host.t()) :: :ok
Start a new host under the Hemdal.Host.Supervisor
module.
@spec start_all() :: :ok
Start all of the hosts based on the configuration. It's retrieving all of
the hosts from the configuration and the using each host with the start/1
function.
@spec start_link([Hemdal.Config.Host.t()]) :: {:ok, pid()}
Start a new host directly. It's intended to be in use from the supervisor, but it could be used for test purposes.
@spec stop(host_id()) :: :ok
Terminate the processes related to a process ID.
@spec update_host(Hemdal.Config.Host.t()) :: {:ok, pid()}
Update the host configuration. If the host isn't running it's starting it
and passing it the configuration provided as the host
parameter. In a
nutshell, it's: if exist host ID then perform an update of the host
information, else start the process with the host information.