Sample content β LAB-0.2 (multi-part PAv1)ΒΆ
This is the multi-part companion to LAB-0.1. Where LAB-0.1 is a
single-part lab (one cml_on_aws pod), LAB-0.2 is a three-part expert exam that exercises
the parts of the model a single-part lab cannot:
- Definition β Instance duality (ADR-050) β
one
SessionDefinition+ threePartDefinitions instantiate into oneSession+ threeSessionParts. - Multi-part session gating (ADR-045) β
each
SessionPartis its ownTimedResourcewith an ordered, gated lifecycle. - Content-authoring taxonomy (ADR-052) β
each part selects a
Formby its FQN pattern, not by a hard-coded id. PodTypevsHostTypesplit (ADR-046) β the three parts use three different provisioning shapes: web (no pod), device (ROC/RADkit, no pod), andcml_on_aws(a realPodInstance).- Authorization (ADR-053) β the session
is guarded by an
AuthorizationPolicywith a JQ-driven claim requirement.
Like
LAB-0.1, this sample is a documentation reference, not a deployed seed. The samePAv1/folder is what gets published to Mosaic and synced into RustFS (see Flow: Content Sync).
The three partsΒΆ
| # | Part | PodType |
Provisioning | Reaches | Form FQN pattern |
|---|---|---|---|---|---|
| 1 | DES β web | none | web form only β no PodInstance |
the candidate's browser | β¦ DES v1 web |
| 2 | DOO β device | none | pre-existing devices via ROC / RADkit β no PodInstance |
managed lab devices | β¦ DOO v1 device |
| 3 | AI-DOO β cml | cml_on_aws |
provisions a PodInstance (TimedResource) |
a CML-on-AWS topology | β¦ AI-DOO v1 cml |
Only Part 3 provisions a runtime pod. Parts 1 and 2 add no new resource kind β they are
SessionPart TimedResources that bind a Form and run the same Collect β Evaluate β Report
automation against either the browser submission (web) or pre-existing devices (ROC/RADkit). This
is exactly the Device deferral noted in the glossary and
ADR-050 Β§2.3: device parts target existing
devices and provision nothing.
flowchart TB
S["Session: EXPERT-LAB-0.2<br/>(SessionDefinition β Session)"]
S --> P1["Part 1 Β· DES web<br/>PodType: none (web form)<br/>Form FQN: β¦DES v1 web"]
S --> P2["Part 2 Β· DOO device<br/>PodType: none (ROC/RADkit)<br/>Form FQN: β¦DOO v1 device"]
S --> P3["Part 3 Β· AI-DOO<br/>PodType: cml_on_aws<br/>Form FQN: β¦AI-DOO v1 cml"]
P1 -->|gates| P2
P2 -->|gates| P3
P3 --> POD["PodInstance (cml_on_aws)<br/>TimedResource Β· ADR-050"]
style P1 fill:#1e3a5f,color:#fff
style P2 fill:#0d9488,color:#fff
style P3 fill:#475569,color:#fff
style POD fill:#2563eb,color:#fff
Folder layoutΒΆ
LAB-0.2/
βββ PAv1/
βββ manifest.yaml # SessionDefinition: session type + 3 part requirements
βββ lifecycle.yaml # session phase order + per-part workflow bindings
βββ authorization/
β βββ policies.yaml # AuthorizationPolicy + Requirements (claim / composite)
βββ parts/
βββ 01-des-web/
β βββ part.yaml # PartDefinition: requirement + Form FQN selector (web)
β βββ grading/rubric.yaml
βββ 02-doo-device/
β βββ part.yaml # PartDefinition: device selector, no pod
β βββ scenarios/collect.yaml
β βββ grading/rubric.yaml
βββ 03-ai-doo-cml/
βββ part.yaml # PartDefinition: cml_on_aws pod
βββ topology/devices.json
βββ topology/ports.json
βββ scenarios/collect.yaml
βββ grading/rubric.yaml
βββ reports/score_report.yaml
How definitions become instancesΒΆ
Definition (in PAv1/) |
β Instance (runtime) | Tier | Source |
|---|---|---|---|
SessionDefinition (manifest) |
Session |
Timed | seed |
PartDefinition Γ 3 (parts/*/part.yaml) |
SessionPart Γ 3 |
Timed | seed |
Form selected by each part's FQN pattern |
delivered by the SessionPart β no instance |
β | content_package |
Part 3 PodDefinition (topology/) |
PodInstance (cml_on_aws) |
Timed | seed |
JobDefinition / ReportDefinition per part |
Job / Report |
Untimed | content_package |
AuthorizationPolicy (authorization/) |
config β no instance | β | seed |
File contentsΒΆ
manifest.yamlΒΆ
# SessionDefinition β the session type and its ordered part requirements.
# CPA reads this when the content is synced; it instantiates one Session + three SessionParts.
apiVersion: pav1
kind: SessionDefinition
metadata:
name: EXPERT-LAB-0.2
title: "Sample Expert Multi-Part Exam"
track: "999-001 EXPERT DEMO"
exam: "999-001"
language: ENU
content_version: "1"
spec:
authorization_policy: expert-candidates # -> authorization/policies.yaml
parts: # ordered; each gates the next
- requirement: des-web # -> parts/01-des-web/part.yaml
order: 1
- requirement: doo-device # -> parts/02-doo-device/part.yaml
order: 2
- requirement: ai-doo-cml # -> parts/03-ai-doo-cml/part.yaml
order: 3
lifecycle.yamlΒΆ
# Session-level phase order (CPA) + the per-part workflow each part runs (SE).
# The Session reconciles its parts in `order`; a part gates the next only when it reaches `graded`.
apiVersion: pav1
kind: Lifecycle
metadata:
session: EXPERT-LAB-0.2
spec:
session_phases: [pending, active, inactive] # driven by the Session's own Timeslot
part_workflow: # applied to every SessionPart
- name: provision
# web/device parts skip pod provisioning; cml part runs native instantiate steps
native_steps_by_pod_type:
none: [mark_ready]
cml_on_aws: [worker_lab_resolve, pod_locator, ports_alloc, lds_register, mark_ready]
- name: collect
jobs:
- definition: collect_evidence@v1
process_type: Grading
scenario: collect # absent for the web part (browser submission)
- name: grade
jobs:
- definition: grade_item@v1
process_type: Grading
rubric: rubric # -> parts/<part>/grading/rubric.yaml
- name: report
jobs:
- definition: score_report@v1
process_type: Grading
report: score_report # session-level aggregate (see Part 3)
- name: teardown
native_steps_by_pod_type:
none: [archive]
cml_on_aws: [archive, lab_wipe]
Canonical lifecycle shape.
native_steps_by_pod_type+part_workflowis the canonical multi-part form (ADR-057 Β§2.6): a session applies the samejobs[]to everySessionPart, varying only the native steps by pod type. Each job references aJobDefinition(definition: name@version) whose step DAG lives under the part'sjobs/. A single-part lab (LAB-0.1) is the same shape with onecml_on_awsentry; theLAB-1.1.1golden port shows the full step DAGs.
authorization/policies.yamlΒΆ
# AuthorizationPolicy β guards the session. Keycloak/OIDC stays the AUTHN layer;
# this adds fine-grained AUTHZ filtering (ADR-053). Admin principals bypass all checks.
apiVersion: pav1
kind: AuthorizationPolicy
metadata:
name: expert-candidates
spec:
requirements:
- type: All
of:
- type: Claim
claim_type: "exam_registration"
# JQ runtime expression evaluated against the entity under check:
# the user's exam_registration claim must equal this session's exam id.
claim_value: ".exam"
- type: Claim
claim_type: "expert_eligible" # existence check (no value)
parts/01-des-web/part.yamlΒΆ
# PartDefinition β a WEB part. Selects a Form by FQN PATTERN (not a hard id), delivers it in the
# browser, and provisions NO pod. The part IS the runtime that delivers the form.
apiVersion: pav1
kind: PartDefinition
metadata:
name: des-web
spec:
pod_type: none
form_selector:
fqn_pattern: "999-001 EXPERT DEMO * DES v1 web" # six-token FQN, glob on module
collect_source: submission # graded from the candidate's browser submission
parts/01-des-web/grading/rubric.yamlΒΆ
apiVersion: pav1
kind: EvaluationRuleset
metadata:
name: rubric
spec:
domain: "Design (DES)"
max_points: 4
items:
- id: 1
points: 2
description: "Submitted design names a redundant default gateway"
checks:
- source: submission.answers.gateway
regex: 'HSRP|VRRP|GLBP'
- id: 2
points: 2
description: "Submitted design documents an OSPF area plan"
checks:
- source: submission.answers.routing
regex: 'area\s+0'
flags: [ignorecase]
parts/02-doo-device/part.yamlΒΆ
# PartDefinition β a DEVICE part. Targets PRE-EXISTING devices via ROC/RADkit; provisions NO pod
# and adds NO new resource kind (the Device deferral, ADR-050 Β§2.3).
apiVersion: pav1
kind: PartDefinition
metadata:
name: doo-device
spec:
pod_type: none
form_selector:
fqn_pattern: "999-001 EXPERT DEMO * DOO v1 device"
devices:
transport: roc_radkit # access/collection transport, NOT a PodType
inventory: expert-rack-a # a pre-existing managed device inventory
parts/02-doo-device/scenarios/collect.yamlΒΆ
apiVersion: pav1
kind: ScenarioDefinition
metadata:
name: collect
spec:
devices: [edge01, core01]
collect:
- device: edge01
command: "show ip route ospf"
as: edge01.show_ip_route_ospf
- device: core01
command: "show running-config | section bgp"
as: core01.show_bgp
parts/02-doo-device/grading/rubric.yamlΒΆ
apiVersion: pav1
kind: EvaluationRuleset
metadata:
name: rubric
spec:
domain: "Deploy & Operate (DOO)"
max_points: 4
items:
- id: 1
points: 2
description: "edge01 learns the core loopback via OSPF"
checks:
- source: edge01.show_ip_route_ospf
regex: 'O\s+10\.255\.0\.1/32'
- id: 2
points: 2
description: "core01 has an established iBGP session"
checks:
- source: core01.show_bgp
regex: 'neighbor\s+10\.0\.0\.2\s+remote-as\s+65000'
flags: [ignorecase]
parts/03-ai-doo-cml/part.yamlΒΆ
# PartDefinition β a CML_ON_AWS part. This is the ONLY part that provisions a PodInstance.
apiVersion: pav1
kind: PartDefinition
metadata:
name: ai-doo-cml
spec:
pod_type: cml_on_aws
host_type: cml_on_aws # platform axis (ADR-046) β where the pod runs
form_selector:
fqn_pattern: "999-001 EXPERT DEMO * AI-DOO v1 cml"
parts/03-ai-doo-cml/topology/devices.jsonΒΆ
{
"default_instance_type": "m5zn.metal",
"device_type": "cml",
"ami_name": "cisco-cml2.9.1-20nodes-v0.2.2c",
"disk_type": "io1",
"disk_size": 256,
"public_ip": 1,
"lab_type": "lablet"
}
parts/03-ai-doo-cml/topology/ports.jsonΒΆ
{
"comment": "Per-device console/serial/vnc ports for the cml_on_aws pod.",
"ports": [
{ "device": "rtr01", "serial": 5045 },
{ "device": "rtr02", "serial": 5046 },
{ "device": "host01", "serial": 5050, "vnc": 5051, "pat": { "5052": 22 } }
]
}
parts/03-ai-doo-cml/scenarios/collect.yamlΒΆ
apiVersion: pav1
kind: ScenarioDefinition
metadata:
name: collect
spec:
devices: [rtr01, rtr02, host01]
collect:
- device: rtr01
command: "show ip ospf neighbor"
as: rtr01.show_ip_ospf_nei
- device: host01
command: "python3 /opt/verify_intent.py"
as: host01.intent_check
parts/03-ai-doo-cml/grading/rubric.yamlΒΆ
apiVersion: pav1
kind: EvaluationRuleset
metadata:
name: rubric
spec:
domain: "AI-Assisted Deploy & Operate (AI-DOO)"
max_points: 4
items:
- id: 1
points: 2
description: "rtr01 has an OSPF neighbor in FULL state"
checks:
- source: rtr01.show_ip_ospf_nei
regex: 'FULL'
flags: [ignorecase]
- id: 2
points: 2
description: "Intent-verification script reports success"
checks:
- source: host01.intent_check
regex: 'INTENT:\s*PASS'
parts/03-ai-doo-cml/reports/score_report.yamlΒΆ
# Session-level aggregate ScoreReport. SE emits one result per part, then totals across parts.
apiVersion: pav1
kind: ProcessReportSpec
metadata:
name: score_report
spec:
process_type: Grading
report_class: ScoreReport
aggregate: by_part # one block per SessionPart, plus a session total
include:
- per_part: [part, points_awarded, points_max, percentage]
- per_item: [id, description, points_awarded, points_max, passed, issues]
- totals: [points_awarded, points_max, percentage]
RelatedΒΆ
LAB-0.1β the single-part baseline this example extends.- Session Model β
PartDefinitionselectors and thePodType/HostTypesplit. - Definition & Catalogue Model β provisioning sources and the content taxonomy.
- Resource Model β the
TimedResourcetree the parts and pod live in.