Skip to content

Commit

Permalink
Don't configure if interface doesn't exist
Browse files Browse the repository at this point in the history
  • Loading branch information
fhunleth committed May 9, 2019
1 parent 0ac97a5 commit 94b8a27
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 79 deletions.
192 changes: 130 additions & 62 deletions lib/vintage_net/interface.ex
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ defmodule VintageNet.Interface do
initial_data = %State{ifname: ifname, config: null_raw_config(ifname)}
update_properties(:configured, initial_data)

VintageNet.subscribe(["interface", ifname, "present"])

actions =
case load_config(ifname) do
{:ok, saved_raw_config} ->
Expand Down Expand Up @@ -363,120 +365,182 @@ defmodule VintageNet.Interface do
:info,
{:commands_done, :ok},
:reconfiguring,
%State{config: config, next_config: new_config} = data
%State{config: old_config, next_config: new_config} = data
) do
# TODO
_ = Logger.debug(":reconfiguring -> done success")
rm(config.cleanup_files)
CommandRunner.remove_files(config.files)
CommandRunner.create_files(new_config.files)
new_data = run_commands(data, new_config.up_cmds)
new_data = %{new_data | config: new_config, next_config: nil}

action = [
{:state_timeout, new_config.up_cmd_millis, :configuring_timeout}
]
_ = Logger.debug("#{data.ifname}:reconfiguring -> cleanup success")
rm(old_config.cleanup_files)
CommandRunner.remove_files(old_config.files)

data = %{data | config: new_config, next_config: nil}

if interface_available?(data) do
rm(new_config.cleanup_files)
CommandRunner.create_files(new_config.files)
new_data = run_commands(data, new_config.up_cmds)

actions = [
{:state_timeout, new_config.up_cmd_millis, :configuring_timeout}
]

update_properties(:configuring, new_data)
{:next_state, :configuring, new_data, action}
update_properties(:configuring, new_data)
{:next_state, :configuring, new_data, actions}
else
{new_data, actions} = reply_to_waiters(data)
new_data = %{new_data | command_runner: nil}
actions = [{:state_timeout, new_config.retry_millis, :retry_timeout} | actions]
update_properties(:retrying, new_data)
{:next_state, :retrying, new_data, actions}
end
end

@impl true
def handle_event(
:info,
{:commands_done, {:error, _reason}},
:reconfiguring,
%State{config: config, next_config: new_config} = data
%State{config: old_config, next_config: new_config} = data
) do
# TODO
_ = Logger.debug(":reconfiguring -> done error")
rm(config.cleanup_files)
CommandRunner.remove_files(config.files)
CommandRunner.create_files(new_config.files)
new_data = run_commands(data, new_config.up_cmds)
new_data = %{new_data | config: new_config, next_config: nil}

action = [
{:state_timeout, new_config.up_cmd_millis, :configuring_timeout}
]
rm(old_config.cleanup_files)
CommandRunner.remove_files(old_config.files)

data = %{data | config: new_config, next_config: nil}

update_properties(:configuring, new_data)
{:next_state, :configuring, new_data, action}
if interface_available?(data) do
rm(new_config.cleanup_files)
CommandRunner.create_files(new_config.files)
new_data = run_commands(data, new_config.up_cmds)

actions = [
{:state_timeout, new_config.up_cmd_millis, :configuring_timeout}
]

update_properties(:configuring, new_data)
{:next_state, :configuring, new_data, actions}
else
{new_data, actions} = reply_to_waiters(data)
new_data = %{new_data | command_runner: nil}
actions = [{:state_timeout, new_config.retry_millis, :retry_timeout} | actions]
update_properties(:retrying, new_data)
{:next_state, :retrying, new_data, actions}
end
end

@impl true
def handle_event(
:info,
{:EXIT, pid, reason},
:reconfiguring,
%State{config: config, command_runner: pid, next_config: new_config} = data
%State{config: old_config, command_runner: pid, next_config: new_config} = data
) do
# TODO
_ = Logger.debug(":reconfiguring -> done crash (#{inspect(reason)})")
rm(config.cleanup_files)
CommandRunner.remove_files(config.files)
CommandRunner.create_files(new_config.files)
new_data = run_commands(data, new_config.up_cmds)
new_data = %{new_data | config: new_config, next_config: nil}

action = [
{:state_timeout, new_config.up_cmd_millis, :configuring_timeout}
]
rm(old_config.cleanup_files)
CommandRunner.remove_files(old_config.files)
data = %{data | config: new_config, next_config: nil}

if interface_available?(data) do
rm(new_config.cleanup_files)
CommandRunner.create_files(new_config.files)
new_data = run_commands(data, new_config.up_cmds)

update_properties(:configuring, new_data)
{:next_state, :configuring, new_data, action}
actions = [
{:state_timeout, new_config.up_cmd_millis, :configuring_timeout}
]

update_properties(:configuring, new_data)
{:next_state, :configuring, new_data, actions}
else
{new_data, actions} = reply_to_waiters(data)
new_data = %{new_data | command_runner: nil}
actions = [{:state_timeout, new_config.retry_millis, :retry_timeout} | actions]
update_properties(:retrying, new_data)
{:next_state, :retrying, new_data, actions}
end
end

@impl true
def handle_event(
:state_timeout,
_event,
:reconfiguring,
%State{command_runner: pid, config: config, next_config: new_config} = data
%State{command_runner: pid, config: old_config, next_config: new_config} = data
) do
_ = Logger.debug(":reconfiguring -> recovering from hang")
Process.exit(pid, :kill)
rm(config.cleanup_files)
CommandRunner.remove_files(config.files)
CommandRunner.create_files(new_config.files)
new_data = run_commands(data, new_config.up_cmds)
new_data = %{new_data | config: new_config, next_config: nil}

action = [
{:state_timeout, new_config.up_cmd_millis, :configuring_timeout}
]
rm(old_config.cleanup_files)
CommandRunner.remove_files(old_config.files)

data = %{data | config: new_config, next_config: nil}

if interface_available?(data) do
rm(new_config.cleanup_files)
CommandRunner.create_files(new_config.files)
new_data = run_commands(data, new_config.up_cmds)

actions = [
{:state_timeout, new_config.up_cmd_millis, :configuring_timeout}
]

update_properties(:configuring, new_data)
{:next_state, :configuring, new_data, action}
update_properties(:configuring, new_data)
{:next_state, :configuring, new_data, actions}
else
{new_data, actions} = reply_to_waiters(data)
new_data = %{new_data | command_runner: nil}
actions = [{:state_timeout, new_config.retry_millis, :retry_timeout} | actions]
update_properties(:retrying, new_data)
{:next_state, :retrying, new_data, actions}
end
end

# :retrying

@impl true
def handle_event(:state_timeout, _event, :retrying, %State{config: config} = data) do
CommandRunner.create_files(config.files)
new_data = run_commands(data, config.up_cmds)
update_properties(:configuring, new_data)

{:next_state, :configuring, new_data,
{:state_timeout, config.up_cmd_millis, :configuring_timeout}}
def handle_event(:state_timeout, _event, :retrying, %State{config: new_config} = data) do
if interface_available?(data) do
rm(new_config.cleanup_files)
CommandRunner.create_files(new_config.files)
new_data = run_commands(data, new_config.up_cmds)

actions = [
{:state_timeout, new_config.up_cmd_millis, :configuring_timeout}
]

update_properties(:configuring, new_data)
{:next_state, :configuring, new_data, actions}
else
{:keep_state, data, {:state_timeout, new_config.retry_millis, :retry_timeout}}
end
end

@impl true
def handle_event(
{:call, from},
{:configure, config},
{:configure, new_config},
:retrying,
data
) do
_ = Logger.debug(":retrying -> configure")

CommandRunner.create_files(config.files)
new_data = run_commands(data, config.up_cmds)
update_properties(:configuring, new_data)
data = %{data | config: new_config}
actions = [{:reply, from, :ok}]

if interface_available?(data) do
rm(new_config.cleanup_files)
CommandRunner.create_files(new_config.files)
new_data = run_commands(data, new_config.up_cmds)

actions = [
{:state_timeout, new_config.up_cmd_millis, :configuring_timeout} | actions
]

{:next_state, :configuring, new_data,
[{:reply, from, :ok}, {:state_timeout, config.up_cmd_millis, :configuring_timeout}]}
update_properties(:configuring, new_data)
{:next_state, :configuring, new_data, actions}
else
{:keep_state, data, [{:state_timeout, new_config.retry_millis, :retry_timeout} | actions]}
end
end

# Catch all event handlers
Expand Down Expand Up @@ -591,4 +655,8 @@ defmodule VintageNet.Interface do

{%{data | inflight_ioctls: %{}}, actions}
end

defp interface_available?(data) do
not data.config.require_interface or VintageNet.get(["interface", data.ifname, "present"])
end
end
3 changes: 3 additions & 0 deletions lib/vintage_net/interface/raw_config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ defmodule VintageNet.Interface.RawConfig do
* `ifname` - the name of the interface (e.g., `"eth0"`)
* `type` - the type of network interface (aka the module that created the config)
* `source_config` - the configuration that generated this one
* `require_interface` - require the interface to exist in the system before configuring
* `retry_millis` - if bringing the interface up fails, wait this amount of time before retrying
* `files` - a list of file path, content tuples
* `up_cmd_millis` - the maximum amount of time to allow the up command list to take
Expand All @@ -28,6 +29,7 @@ defmodule VintageNet.Interface.RawConfig do
defstruct ifname: nil,
type: nil,
source_config: %{},
require_interface: true,
retry_millis: 30_000,
files: [],
child_specs: [],
Expand All @@ -41,6 +43,7 @@ defmodule VintageNet.Interface.RawConfig do
ifname: VintageNet.ifname(),
type: atom(),
source_config: map(),
require_interface: boolean(),
retry_millis: non_neg_integer(),
files: [file_contents()],
child_specs: [Supervisor.child_spec()],
Expand Down
3 changes: 2 additions & 1 deletion lib/vintage_net/technology/null.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ defmodule VintageNet.Technology.Null do
%RawConfig{
ifname: ifname,
type: __MODULE__,
source_config: %{type: __MODULE__}
source_config: %{type: __MODULE__},
require_interface: false
}}
end

Expand Down
Loading

0 comments on commit 94b8a27

Please sign in to comment.