Class: Syskit::NetworkGeneration::SystemNetworkDeployer

Inherits:
Object
  • Object
show all
Extended by:
Logger::Hierarchy
Includes:
Logger::Hierarchy, Roby::DRoby::EventLogging
Defined in:
lib/syskit/network_generation/system_network_deployer.rb

Overview

Algorithm that transforms a network generated by SystemNetworkGenerator into a deployed network

It does not deal with adapting an existing network

Defined Under Namespace

Classes: DeploymentGroupVisitor

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(plan, event_logger: plan.event_logger, merge_solver: MergeSolver.new(plan), default_deployment_group: Syskit.conf.deployment_group) ⇒ SystemNetworkDeployer

Returns a new instance of SystemNetworkDeployer



36
37
38
39
40
41
42
43
44
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 36

def initialize(plan, event_logger: plan.event_logger,
        merge_solver: MergeSolver.new(plan),
        default_deployment_group: Syskit.conf.deployment_group)

    @plan = plan
    @event_logger = event_logger
    @merge_solver = merge_solver
    @default_deployment_group = default_deployment_group
end

Instance Attribute Details

#default_deployment_groupModels::DeploymentGroup

The deployment group used by default

Each subpart of the network can specify their own through Component#requirements, in which case the new group is merged into the default



34
35
36
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 34

def default_deployment_group
  @default_deployment_group
end

#event_loggerObject (readonly)

An event logger object used to track execution

See Also:

  • Syskit::NetworkGeneration::SystemNetworkDeployer.{Roby{Roby::DRoby{Roby::DRoby::EventLogging}


20
21
22
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 20

def event_logger
  @event_logger
end

#merge_solverMergeSolver (readonly)

The solver used to track the deployed tasks vs. the original tasks

Returns:



25
26
27
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 25

def merge_solver
  @merge_solver
end

#planRoby::Plan (readonly)

The plan this deployer is acting on

Returns:

  • (Roby::Plan)


15
16
17
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 15

def plan
  @plan
end

Instance Method Details

#apply_selected_deployments(selected_deployments) ⇒ void

This method returns an undefined value.

Modify the plan to apply a deployment selection

Parameters:



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 300

def apply_selected_deployments(selected_deployments)
    deployment_tasks = Hash.new
    selected_deployments.each do |task, (configured_deployment, task_name)|
        deployment_task = (deployment_tasks[[configured_deployment]] ||=
                configured_deployment.new)

        if Syskit.conf.permanent_deployments?
            plan.add_permanent_task(deployment_task)
        else
            plan.add(deployment_task)
        end
        deployed_task = deployment_task.task(task_name)
        debug { "deploying #{task} with #{task_name} of "\
            "#{configured_deployment.short_name} (#{deployed_task})" }
        # We MUST merge one-by-one here. Calling apply_merge_group
        # on all the merges at once would NOT copy the connections
        # that exist between the tasks of the "from" group to the
        # "to" group, which is really not what we want
        #
        # Calling with all the mappings would be useful if what
        # we wanted is replace a subnet of the plan by another
        # subnet. This is not the goal here.
        merge_solver.apply_merge_group(task => deployed_task)
    end
end

#deploy(validate: true) ⇒ Set

Replace non-deployed tasks in the plan by deployed ones

The task-to-deployment association is handled by the network's deployment groups (accessible through Component#requirements) as well as the default deployment group (#default_deployment_group)

Parameters:

  • validate (Boolean)

    if true, #validate_deployed_networks will run on the generated network

Returns:

  • (Set)

    the set of tasks for which the deployer could not find a deployment



56
57
58
59
60
61
62
63
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
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 56

def deploy(validate: true)
    debug "Deploying the system network"

    deployment_groups = propagate_deployment_groups

    debug do
        debug "Deployment candidates"
        log_nest(2) do
            deployment_groups.each do |task, group|
                candidates = group.
                    find_all_suitable_deployments_for(task)
                log_pp :debug, task
                log_nest(2) do
                    if candidates.empty?
                        debug "no deployments"
                    else
                        candidates.each do |deployment|
                            log_pp :debug, deployment
                        end
                    end
                end
            end
        end
        break
    end

    all_tasks = plan.find_local_tasks(TaskContext).to_a
    selected_deployments, missing_deployments =
        select_deployments(all_tasks, deployment_groups)
    log_timepoint 'select_deployments'

    apply_selected_deployments(selected_deployments)
    log_timepoint 'apply_selected_deployments'

    if validate
        validate_deployed_network(deployment_groups)
        log_timepoint 'validate_deployed_network'
    end

    return missing_deployments
end

#find_suitable_deployment_for(task, deployment_groups) ⇒ nil, Deployment

Finds the deployments suitable for a task in a given group

If more than one deployment matches in the group, it calls #resolve_deployment_ambiguity to try and pick one

Parameters:

Returns:



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 237

def find_suitable_deployment_for(task, deployment_groups)
    candidates = deployment_groups[task].
        find_all_suitable_deployments_for(task)

    return candidates.first if candidates.size <= 1

    debug do
        "#{candidates.size} deployments available for #{task} "\
        "(#{task.concrete_model}), trying to resolve"
    end
    selected = log_nest(2) do
        resolve_deployment_ambiguity(candidates, task)
    end
    if selected
        debug { "  selected #{selected}" }
        return selected
    else
        debug do
            "  deployment of #{task} (#{task.concrete_model}) "\
            "is ambiguous"
        end
        return
    end
end

#propagate_deployment_groups(use_cow: true) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Create a hash of task instances to the deployment group that should be used for that instance



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 189

def propagate_deployment_groups(use_cow: true)
    dependency_graph = plan.
        task_relation_graph_for(Roby::TaskStructure::Dependency)

    all_groups = Hash.new
    dependency_graph.each_vertex do |task|
        next unless dependency_graph.root?(task)

        visitor = DeploymentGroupVisitor.new(
            dependency_graph, default_deployment_group, use_cow: use_cow)
        visitor.handle_start_vertex(task)
        dependency_graph.depth_first_visit(task, visitor) {}

        visitor.deployment_groups.each do |task, (_shared, task_group)|
            DeploymentGroupVisitor.update_deployment_groups(
                all_groups, task, task_group, use_cow: use_cow)
        end
    end

    groups = Hash.new
    all_groups.each do |task, (_shared, task_group)|
        groups[task] = task_group
    end

    # 'groups' here includes only the tasks that are in the
    # dependency graph. Make sure we add entries for the rest as
    # well
    plan.find_local_tasks(Syskit::Component).each do |task|
        if !groups.has_key?(task)
            task_group = task.requirements.deployment_group
            groups[task] =
                if task_group.empty?
                    default_deployment_group
                else task_group
                end
        end
    end
    groups
end

#resolve_deployment_ambiguity(candidates, task) ⇒ (Model<Deployment>,String)?

Try to resolve a set of deployment candidates for a given task

Parameters:

  • candidates (Array<(String,Model<Deployment>,String)>)

    set of deployment candidates as (process_server_name,deployment_model,task_name) tuples

  • task (Syskit::TaskContext)

    the task context for which candidates are possible deployments

Returns:

  • ((Model<Deployment>,String), nil)

    the resolved deployment, if finding a single best candidate was possible, or nil otherwise.



373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 373

def resolve_deployment_ambiguity(candidates, task)
    if task.orocos_name
        debug { "#{task} requests orocos_name to be #{task.orocos_name}" }
        resolved = candidates.
            find { |_, task_name| task_name == task.orocos_name }
        if !resolved
            debug { "cannot find requested orocos name #{task.orocos_name}" }
        end
        return resolved
    end
    hints = task.deployment_hints
    debug { "#{task}.deployment_hints: #{hints.map(&:to_s).join(", ")}" }
    # Look to disambiguate using deployment hints
    resolved = candidates.find_all do |deployment_model, task_name|
        task.deployment_hints.any? do |rx|
            rx == deployment_model || rx === task_name
        end
    end
    if resolved.size != 1
        info do
            info { "ambiguous deployment for #{task} (#{task.model})" }
            candidates.each do |deployment_model, task_name|
                info do
                    "  #{task_name} of #{deployment_model.short_name} "\
                    "on #{deployment_model.process_server_name}"
                end
            end
            break
        end
        return
    end
    return resolved.first
end

#select_deployments(tasks, deployment_groups) ⇒ (Component=>Deployment,[Component])

Find which deployments should be used for which tasks

Parameters:

Returns:

  • ((Component=>Deployment,[Component]))

    the association between components and the deployments that should be used for them, and the list of components without deployments



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 271

def select_deployments(tasks, deployment_groups)
    used_deployments = Set.new
    missing_deployments = Set.new
    selected_deployments = Hash.new

    tasks.each do |task|
        next if task.execution_agent
        if !(selected = find_suitable_deployment_for(task, deployment_groups))
            missing_deployments << task
        elsif used_deployments.include?(selected)
            debug do
                machine, configured_deployment, task_name = *selected
                "#{task} resolves to #{configured_deployment}.#{task_name} "\
                    "on #{machine} for its deployment, but it is already used"
            end
            missing_deployments << task
        else
            used_deployments << selected
            selected_deployments[task] = selected
        end
    end
    [selected_deployments, missing_deployments]
end

#validate_deployed_network(deployment_groups) ⇒ Object

Sanity checks to verify that the result of #deploy_system_network is valid

Raises:



330
331
332
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 330

def validate_deployed_network(deployment_groups)
    verify_all_tasks_deployed(deployment_groups)
end

#verify_all_tasks_deployed(deployment_groups) ⇒ Object

Verifies that all tasks in the plan are deployed

Parameters:

  • deployment_groups (Component=>DeploymentGroup)

    which deployment groups has been used for which task. This is used to generate the error messages when needed.



339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/syskit/network_generation/system_network_deployer.rb', line 339

def verify_all_tasks_deployed(deployment_groups)
    not_deployed = plan.find_local_tasks(TaskContext).
        not_finished.not_abstract.
        find_all { |t| !t.execution_agent }

    if !not_deployed.empty?
        tasks_with_candidates = Hash.new
        not_deployed.each do |task|
            candidates = deployment_groups[task].
                find_all_suitable_deployments_for(task)
            candidates = candidates.map do |configured_deployment, task_name|
                existing = plan.find_local_tasks(task.model).
                    find_all { |t| t.orocos_name == task_name }
                [configured_deployment, task_name, existing]
            end

            tasks_with_candidates[task] = candidates
        end
        raise MissingDeployments.new(tasks_with_candidates),
            "there are tasks for which it exists no deployed equivalent: "\
            "#{not_deployed.map { |m| "#{m}(#{m.orogen_model.name})" }}"
    end
end