Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP chore: early deploy at network generation #445

Open
wants to merge 13 commits into
base: transition-to-runkit
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,37 @@ def minitest_set_options(test_task, name)
test_task.options = "#{TESTOPTS} #{minitest_args} -- --simplecov-name=#{name}"
end

Rake::TestTask.new("test:core") do |t|
t.libs << "."
t.libs << "lib"
minitest_set_options(t, "core")
test_files = FileList["test/**/test_*.rb"]
test_files = test_files
.exclude("test/ros/**/*.rb")
.exclude("test/gui/**/*.rb")
.exclude("test/live/**/*.rb")
t.test_files = test_files
t.warning = false
def core(early_deploy: false)
s = ":no-early-deploy"
if early_deploy
s = ":early-deploy"
early_deploy_setup = ["test/features/early_deploy.rb"]
end

Rake::TestTask.new("test:core#{s}") do |t|
t.libs << "."
t.libs << "lib"
minitest_set_options(t, "core")
test_files = FileList["test/**/test_*.rb", *early_deploy_setup]
test_files = test_files
.exclude("test/ros/**/*.rb")
.exclude("test/gui/**/*.rb")
.exclude("test/live/**/*.rb")
t.test_files = test_files
t.warning = false
end
end

desc "Run separate tests that require a live syskit instance"
task "test:live" do
tests = Dir.enum_for(:glob, "test/live/test_*.rb").to_a
unless system(File.join("test", "live", "run"), *tests)
$stderr.puts "live tests failed"
exit 1
end
end

desc "run gui-only tests"
Rake::TestTask.new("test:gui") do |t|
t.libs << "."
t.libs << "lib"
Expand All @@ -57,6 +68,12 @@ Rake::TestTask.new("test:gui") do |t|
t.warning = false
end

core early_deploy: true
core
desc "Run core library tests, excluding GUI and live tests"
task "test:core" => ["test:core:no-early-deploy", "test:core:early-deploy"]

desc "Run all tests"
task "test" => ["test:gui", "test:core", "test:live"]

task "rubocop" do
Expand Down
33 changes: 33 additions & 0 deletions lib/syskit/exceptions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,39 @@ def pretty_print(pp)
end
end

class ConflictingDeploymentAllocationAtNetworkGeneration < SpecError
attr_reader :deployment_to_tasks

def initialize(deployment_to_tasks)
@deployment_to_tasks = deployment_to_tasks
end

def pretty_print(pp)
pp.text "cannot deploy the following tasks"
deployment_to_tasks.each do |deployed_task, tasks|
tasks.each do |task|
pp.nest(2) do
pp.breakable
pp.text "#{task} (#{task.orogen_model.name})"
end
end
pp.breakable
pp.text "because the same "
process_server_name = deployed_task.configured_deployment
.process_server_name
orogen_model = deployed_task.configured_deployment
.orogen_model
pp.text(
"deployed task #{deployed_task.mapped_task_name} from deployment " \
"#{orogen_model.name} defined in " \
"#{orogen_model.project.name} on #{process_server_name}"
)

pp.text " is allocated for them"
end
end
end

# Exception raised at the end of #resolve if some tasks do not have a
# deployed equivalent
class MissingDeployments < SpecError
Expand Down
26 changes: 20 additions & 6 deletions lib/syskit/network_generation/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -703,12 +703,20 @@ def compute_system_network(
Engine.discover_requirement_tasks_from_plan(real_plan),
garbage_collect: true,
validate_abstract_network: true,
validate_generated_network: true
validate_generated_network: true,
default_deployment_group: nil,
validate_deployed_network: false,
early_deploy: false
)
requirement_tasks = requirement_tasks.to_a
instance_requirements = requirement_tasks.map(&:requirements)
system_network_generator = SystemNetworkGenerator.new(
work_plan, event_logger: event_logger, merge_solver: merge_solver
work_plan,
event_logger: event_logger,
merge_solver: merge_solver,
default_deployment_group: default_deployment_group,
early_deploy: early_deploy,
validate_deployed_network: validate_deployed_network
)
toplevel_tasks = system_network_generator.generate(
instance_requirements,
Expand Down Expand Up @@ -749,14 +757,18 @@ def resolve_system_network(
validate_deployed_network: true,
compute_deployments: true,
default_deployment_group: Syskit.conf.deployment_group,
compute_policies: true
compute_policies: true,
early_deploy: Syskit.conf.early_deploy?
)

required_instances = compute_system_network(
requirement_tasks,
garbage_collect: garbage_collect,
validate_abstract_network: validate_abstract_network,
validate_generated_network: validate_generated_network
validate_generated_network: validate_generated_network,
default_deployment_group: (default_deployment_group if early_deploy),
validate_deployed_network: validate_deployed_network,
early_deploy: early_deploy && compute_deployments
)

if compute_deployments
Expand Down Expand Up @@ -804,7 +816,8 @@ def resolve(
validate_abstract_network: true,
validate_generated_network: true,
validate_deployed_network: true,
validate_final_network: true
validate_final_network: true,
early_deploy: Syskit.conf.early_deploy?
)
required_instances = resolve_system_network(
requirement_tasks,
Expand All @@ -814,7 +827,8 @@ def resolve(
compute_deployments: compute_deployments,
default_deployment_group: default_deployment_group,
compute_policies: compute_policies,
validate_deployed_network: validate_deployed_network
validate_deployed_network: validate_deployed_network,
early_deploy: early_deploy
)

apply_system_network_to_plan(
Expand Down
20 changes: 19 additions & 1 deletion lib/syskit/network_generation/merge_solver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class MergeSolver
# information
attr_reader :event_logger

attr_writer :merge_when_identical_agents

def initialize(plan, event_logger: plan.event_logger)
@plan = plan
@event_logger = event_logger
Expand All @@ -43,6 +45,7 @@ def initialize(plan, event_logger: plan.event_logger)
@task_replacement_graph = Roby::Relations::BidirectionalDirectedAdjacencyGraph.new
@resolved_replacements = {}
@invalid_merges = Set.new
@merge_when_identical_agents = false
end

def clear
Expand All @@ -51,6 +54,10 @@ def clear
@invalid_merges.clear
end

def merge_when_identical_agents?
@merge_when_identical_agents
end

# Returns the task that is used in place of the given task
#
# @param [Roby::Task] the task for which we want to know the
Expand Down Expand Up @@ -138,6 +145,7 @@ def apply_merge_group(merged_task_to_task)

merged_task_to_task.each do |merged_task, task|
unless merged_task.transaction_proxy?
plan.copy_task_marks(from: merged_task, to: task)
plan.remove_task(merged_task)
end
register_replacement(merged_task, task)
Expand Down Expand Up @@ -221,14 +229,24 @@ def may_merge_task_contexts?(merged_task, task)

# Merges involving a deployed task can only involve a
# non-deployed task as well
if task.execution_agent && merged_task.execution_agent
unless mergeable_agents?(merged_task, task)
info "rejected: deployment attribute mismatches"
return false
end

true
end

def mergeable_agents?(merged_task, task)
return true unless task.execution_agent && merged_task.execution_agent

return false unless merge_when_identical_agents?

return true if task.execution_agent == merged_task.execution_agent

false
end

def each_component_merge_candidate(task)
# Get the set of candidates. We are checking if the tasks in
# this set can be replaced by +task+
Expand Down
16 changes: 6 additions & 10 deletions lib/syskit/network_generation/system_network_deployer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ def initialize(plan,
# will run on the generated network
# @return [Set] the set of tasks for which the deployer could
# not find a deployment
def deploy(validate: true)
def deploy(validate: true, reuse_deployments: false, deployment_tasks: {})
debug "Deploying the system network"

all_tasks = plan.find_local_tasks(TaskContext).to_a
selected_deployments, missing_deployments =
select_deployments(all_tasks)
select_deployments(all_tasks, reuse: reuse_deployments)
log_timepoint "select_deployments"

apply_selected_deployments(selected_deployments)
apply_selected_deployments(selected_deployments, deployment_tasks)
log_timepoint "apply_selected_deployments"

if validate
Expand Down Expand Up @@ -132,13 +132,10 @@ def find_suitable_deployment_for(task)
# Find which deployments should be used for which tasks
#
# @param [[Component]] tasks the tasks to be deployed
# @param [Component=>Models::DeploymentGroup] the association
# between a component and the group that should be used to
# deploy it
# @return [(Component=>Deployment,[Component])] the association
# between components and the deployments that should be used
# for them, and the list of components without deployments
def select_deployments(tasks)
def select_deployments(tasks, reuse: false)
used_deployments = Set.new
missing_deployments = Set.new
selected_deployments = {}
Expand All @@ -150,7 +147,7 @@ def select_deployments(tasks)

if !selected
missing_deployments << task
elsif used_deployments.include?(selected)
elsif !reuse && used_deployments.include?(selected)
debug do
machine, configured_deployment, task_name = *selected
"#{task} resolves to #{configured_deployment}.#{task_name} " \
Expand All @@ -170,8 +167,7 @@ def select_deployments(tasks)
# @param [Component=>Deployment] selected_deployments the
# component-to-deployment association
# @return [void]
def apply_selected_deployments(selected_deployments)
deployment_tasks = {}
def apply_selected_deployments(selected_deployments, deployment_tasks = {})
selected_deployments.each do |task, deployed_task|
deployed_task, = deployed_task.instanciate(
plan,
Expand Down
Loading