CORD-714
initial set of roles/playbooks
bug fixes
fix docker-compose logging, pull xos-base image
dynamically add docker instances to ansible inventory after onboarding
Bootstrap the onboarding synchronizer
more defaults, reload vars after adding docker inventory
move TOSCA templates to cord-profilea, random password on admin
fixes for docker compose, paths in xos.yaml in jinja
don't try to mangle XOS _config files (yet)
create xos-test image
make xos-test use locally build xosproject/xos
add docker-compose v2 format networking
fix docker-compose template
path fixes, move ssh keys
service names/paths aren't so simple
added api-tests, teardown roles
scan the onboarded docker-compose file for ansible inventory
add various tests used by test-standalone profile
fixes for API tests
save test output to /tmp/<testname>.out, bugfixes
autogenerate openstack keystone admin password, fix tests
remove nonfunctional UI tests
change location of cord_profile, use inventory to specify profile
fix YAML escaping of backslashes in regex
bugfixes after path change, add teardown playbook
backout setting of cord_dir with ansible_user_dir which differs depending on context
gradle build fixes, renaming
fix yml/yaml naming issue
null xos_images default
added rcord/mcord frontend variants, exampleservice onboarding
add missing role, help text in cord-bootstrap.sh
bugfix
create/run deployment.yaml by default
allow teardown to handle partially built pods, bugfix to deployment.yaml generation
add defaults, fix path for exampleservice
revert yaml naming to ease testing, rename mocks
debugging
exampleservice onboarding, mounting volume in XOS container
bugfix
add volume mounts when creating xos_ui, don't double add to ansible inventory
post-onboard TOSCA cnfig
typo fixes, order of loading TOSCA
config bits for cord-pod, some var renaming
update documentation, rename to rcord
doc fixes
support for building just before XOS install, docs
fix tests, refactor how compute nodes are configed, split vtn service config from adding a node
remove build process from deploy repo
inclusion/merge of PKI support
typo
bugfixes and change to use cord instead of opencord for install dir
fix pki support
fix ssh key paths
update xos ui/bs ports, fix onboarding on vagrant
have compute enlist script use same config file as other playbooks
fix ports, add MaaS version of compute node enable script
fix port and nodes.yaml loading
generate API SSL cert for all profiles
remove cord-app-build which is vestigial
remove config dir
default xos_ui_port in xos-ready role
use xostosca from service-profie/cord-pod-ansible to handle POST form-encode
fix nodes.yaml, variable name in xostosca, and include openstack properly
copy cert chain to build into XOS container
increase onboarding timeouts, don't restart docker
fix ONOS app versions and network settings
fix management_hosts network optional include
fix management/fabric settings
avoid modifying service#ONOS_CORD when adding nodes
split out compute node and vtn config, put delay between
fix template generation and fail on file not found
rename vars to profile_manifests, fix redis include
whitespace fix
increase timeout
reenable platform-check
parameterize node_key path, set defaults and fix platform-check
workaround for onboarding sync, minor fixes
pause in middle of VTN bug workaround
reload openstack config as well
disable platform-check role as a test
fixed head-diag role
reapply VTN config during compute node enable
Create exampleservice instance during test

Change-Id: I87e171bcfa429e65e1075a1ee4c97de1e90a7dd5
diff --git a/roles/api-test-prep/tasks/main.yml b/roles/api-test-prep/tasks/main.yml
new file mode 100644
index 0000000..7878862
--- /dev/null
+++ b/roles/api-test-prep/tasks/main.yml
@@ -0,0 +1,14 @@
+---
+# api-test-prep/tasks/main.yml
+
+- name: Install node packages for api tests with npm
+  command: "npm install --production"
+  args:
+    chdir: "/opt/xos/tests/api"
+  tags:
+    - skip_ansible_lint # run during testing only
+
+- name: Install dredd-hooks with pip
+  pip:
+    name: dredd-hooks
+
diff --git a/roles/api-tests/defaults/main.yml b/roles/api-tests/defaults/main.yml
new file mode 100644
index 0000000..f78a3b2
--- /dev/null
+++ b/roles/api-tests/defaults/main.yml
@@ -0,0 +1,4 @@
+---
+# api-tests/defaults/main.yml
+
+cord_dir: "{{ hostvars['localhost']['ansible_user_dir'] + '/cord' }}"
diff --git a/roles/api-tests/tasks/main.yml b/roles/api-tests/tasks/main.yml
new file mode 100644
index 0000000..843a384
--- /dev/null
+++ b/roles/api-tests/tasks/main.yml
@@ -0,0 +1,25 @@
+---
+# api-tests/tasks/main.yml
+
+- name: Copy apiary.apib to target location
+  copy:
+    src: "{{ cord_dir }}/orchestration/xos/apiary.apib"
+    dest: "/opt/xos/tests/api/apiary.apib"
+
+- name: Run API tests
+  command: "npm test"
+  args:
+    chdir: "/opt/xos/tests/api"
+  register: api_tests_out
+  ignore_errors: yes
+  tags:
+    - skip_ansible_lint # run during testing only
+
+- name: Save output from API tests
+  copy:
+    content: "{{ api_tests_out.stdout_lines }}"
+    dest: "/tmp/api-tests.out"
+
+- name: Print output from API test
+  debug: var=api_tests_out.stdout_lines
+
diff --git a/roles/automation-integration/templates/do-enlist-compute-node.j2 b/roles/automation-integration/templates/do-enlist-compute-node.j2
index 1c96cce..3490c8c 100644
--- a/roles/automation-integration/templates/do-enlist-compute-node.j2
+++ b/roles/automation-integration/templates/do-enlist-compute-node.j2
@@ -3,6 +3,7 @@
 ID=$1
 HOSTNAME=$2
 LOG=/etc/maas/ansible/logs/$ID.log
+COMPUTE_USER=ubuntu
 
 INV=$(mktemp)
 cat >$INV <<EO_INV
@@ -10,17 +11,17 @@
 juju-head-node.cord.lab ansible_user={{ ansible_user_id }}
 
 [compute]
-$HOSTNAME ansible_user=ubuntu
+$HOSTNAME ansible_user=$COMPUTE_USER
 EO_INV
 
 echo "BEGIN INVENTORY FILE" >> $LOG
 cat $INV >> $LOG
 echo "END INVENTORY_FILE" >> $LOG
 
-echo "cd /opt/cord/build/platform-install; ansible-playbook --private-key=/etc/maas/ansible/id_rsa --extra-vars 'cord_in_a_box={{ cord_in_a_box }}' -i $INV cord-compute-playbook.yml" >> $LOG
+echo "cd /opt/cord/build/platform-install; ansible-playbook --private-key=/etc/maas/ansible/id_rsa -u $COMPUTE_USER --extra-vars '@{{ cord_dir }}/build/genconfig/config.yml' -i $INV cord-compute-playbook.yml" >> $LOG
 
 cd /opt/cord/build/platform-install
-ansible-playbook --private-key=/etc/maas/ansible/id_rsa --extra-vars 'cord_in_a_box={{ cord_in_a_box }}' -i $INV cord-compute-playbook.yml >> $LOG
+ansible-playbook --private-key=/etc/maas/ansible/id_rsa -u $COMPUTE_USER --extra-vars '@{{ cord_dir }}/build/genconfig/config.yml' -i $INV cord-compute-maas-playbook.yml >> $LOG
 
 RESULT=$?
 rm $INV
diff --git a/roles/compute-node-config/defaults/main.yml b/roles/compute-node-config/defaults/main.yml
new file mode 100644
index 0000000..70507cc
--- /dev/null
+++ b/roles/compute-node-config/defaults/main.yml
@@ -0,0 +1,14 @@
+---
+# compute-node-config/defaults/main.yml
+
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
+# service configs referenced here are likely located in cord-profile/templates
+
+# used in openstack-compute-vtn.yaml.j2, referencing network in management-net.yaml.j2
+use_management_hosts: False
+vtn_management_host_net_interface: veth3
+
+# used in openstack-compute-vtn.yaml.j2, referencing service in fabric.yaml.j2
+use_fabric: False
+
diff --git a/roles/compute-node-config/tasks/main.yml b/roles/compute-node-config/tasks/main.yml
new file mode 100644
index 0000000..6500dbb
--- /dev/null
+++ b/roles/compute-node-config/tasks/main.yml
@@ -0,0 +1,15 @@
+---
+# compute-node-config/tasks/main.yml
+#
+# Build TOSCA to tell XOS that a new OpenStack compute node has been added
+
+- name: Create OpenStack compute node TOSCA
+  template:
+    src: "{{ item }}.j2"
+    dest: "{{ cord_profile_dir }}/{{ item }}"
+    owner: "{{ ansible_user_id }}"
+    mode: 0644
+  with_items:
+    - openstack-compute.yaml
+    - openstack-compute-vtn.yaml
+
diff --git a/roles/compute-node-config/templates/openstack-compute-vtn.yaml.j2 b/roles/compute-node-config/templates/openstack-compute-vtn.yaml.j2
new file mode 100644
index 0000000..b43e1e3
--- /dev/null
+++ b/roles/compute-node-config/templates/openstack-compute-vtn.yaml.j2
@@ -0,0 +1,116 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+imports:
+   - custom_types/xos.yaml
+
+description: Configures VTN networking for OpenStack compute nodes
+
+topology_template:
+  node_templates:
+
+# VTN ONOS app, fully defined in vtn-service.yaml
+    service#ONOS_CORD:
+      type: tosca.nodes.ONOSService
+      properties:
+        no-delete: true
+        no-create: true
+        no-update: true
+
+{% if use_fabric %}
+# Fabric, fully defined in fabric.yaml
+    service#ONOS_Fabric:
+      type: tosca.nodes.ONOSService
+      properties:
+        no-delete: true
+        no-create: true
+        no-update: true
+{% endif %}
+
+# VTN networking for OpenStack Compute Nodes
+{% for node in groups["compute"] %}
+{% if 'ipv4' in hostvars[node]['ansible_fabric'] %}
+
+# Compute node, fully defined in compute-nodes.yaml
+    {{ hostvars[node]['ansible_hostname'] }}:
+      type: tosca.nodes.Node
+      properties:
+        no-delete: true
+        no-create: true
+        no-update: true
+
+# VTN bridgeId field for node {{ hostvars[node]['ansible_hostname'] }}
+    {{ hostvars[node]['ansible_hostname'] }}_bridgeId_tag:
+      type: tosca.nodes.Tag
+      properties:
+        name: bridgeId
+        value: of:0000{{ hostvars[node]['ansible_fabric']['macaddress'] | hwaddr('bare') }}
+      requirements:
+        - target:
+            node: {{ hostvars[node]['ansible_hostname'] }}
+            relationship: tosca.relationships.TagsObject
+        - service:
+            node: service#ONOS_CORD
+            relationship: tosca.relationships.MemberOfService
+
+# VTN dataPlaneIntf field for node {{ hostvars[node]['ansible_hostname'] }}
+    {{ hostvars[node]['ansible_hostname'] }}_dataPlaneIntf_tag:
+      type: tosca.nodes.Tag
+      properties:
+        name: dataPlaneIntf
+        value: fabric
+      requirements:
+        - target:
+            node: {{ hostvars[node]['ansible_hostname'] }}
+            relationship: tosca.relationships.TagsObject
+        - service:
+            node: service#ONOS_CORD
+            relationship: tosca.relationships.MemberOfService
+
+# VTN dataPlaneIp field for node {{ hostvars[node]['ansible_hostname'] }}
+    {{ hostvars[node]['ansible_hostname'] }}_dataPlaneIp_tag:
+      type: tosca.nodes.Tag
+      properties:
+        name: dataPlaneIp
+        value: {{ ( hostvars[node]['ansible_fabric']['ipv4']['address'] ~ '/' ~ hostvars[node]['ansible_fabric']['ipv4']['netmask'] ) | ipaddr('cidr') }}
+      requirements:
+        - target:
+            node: {{ hostvars[node]['ansible_hostname'] }}
+            relationship: tosca.relationships.TagsObject
+        - service:
+            node: service#ONOS_CORD
+            relationship: tosca.relationships.MemberOfService
+
+{% if use_management_hosts %}
+    # VTN management interface field for node {{ hostvars[node]['ansible_hostname'] }}
+    {{ hostvars[node]['ansible_hostname'] }}_hostManagementIface_tag:
+      type: tosca.nodes.Tag
+      properties:
+        name: hostManagementIface
+        value: {{ vtn_management_host_net_interface }}
+      requirements:
+        - target:
+            node: {{ hostvars[node]['ansible_hostname'] }}
+            relationship: tosca.relationships.TagsObject
+        - service:
+            node: service#ONOS_CORD
+            relationship: tosca.relationships.MemberOfService
+{% endif %}
+
+{% if use_fabric %}
+    # Fabric location field for node {{ hostvars[node]['ansible_hostname'] }}
+    {{ hostvars[node]['ansible_hostname'] }}_location_tag:
+      type: tosca.nodes.Tag
+      properties:
+        name: location
+        value: of:0000000000000001/1
+      requirements:
+        - target:
+            node: {{ hostvars[node]['ansible_hostname'] }}
+            relationship: tosca.relationships.TagsObject
+        - service:
+            node: service#ONOS_Fabric
+            relationship: tosca.relationships.MemberOfService
+{% endif %}
+{% endif %}
+{% endfor %}
+
diff --git a/roles/xos-compute-setup/templates/nodes.yaml.j2 b/roles/compute-node-config/templates/openstack-compute.yaml.j2
similarity index 67%
rename from roles/xos-compute-setup/templates/nodes.yaml.j2
rename to roles/compute-node-config/templates/openstack-compute.yaml.j2
index 7ba953b..b0849dc 100644
--- a/roles/xos-compute-setup/templates/nodes.yaml.j2
+++ b/roles/compute-node-config/templates/openstack-compute.yaml.j2
@@ -3,7 +3,7 @@
 imports:
    - custom_types/xos.yaml
 
-description: list of compute nodes, created by platform-install
+description: Adds OpenStack compute nodes
 
 topology_template:
   node_templates:
@@ -11,12 +11,21 @@
 # Site/Deployment, fully defined in deployment.yaml
     {{ site_name }}:
       type: tosca.nodes.Site
+      properties:
+        no-delete: true
+        no-create: true
+        no-update: true
 
     {{ deployment_type }}:
       type: tosca.nodes.Deployment
+      properties:
+        no-delete: true
+        no-create: true
+        no-update: true
 
-# compute nodes
+# OpenStack compute nodes
 {% for node in groups["compute"] %}
+{% if 'ipv4' in hostvars[node]['ansible_fabric'] %}
     {{ hostvars[node]['ansible_hostname'] }}:
       type: tosca.nodes.Node
       requirements:
@@ -27,5 +36,6 @@
             node: {{ deployment_type }}
             relationship: tosca.relationships.MemberOfDeployment
 
+{% endif %}
 {% endfor %}
 
diff --git a/roles/compute-node-enable-maas/defaults/main.yml b/roles/compute-node-enable-maas/defaults/main.yml
new file mode 100644
index 0000000..b84e46d
--- /dev/null
+++ b/roles/compute-node-enable-maas/defaults/main.yml
@@ -0,0 +1,9 @@
+---
+# compute-node-enable-maas/defaults/main.yml
+
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
+xos_admin_user: "xosadmin@opencord.org"
+xos_admin_pass: "{{ lookup('password', 'credentials/xosadmin@opencord.org chars=ascii_letters,digits') }}"
+
+xos_ui_port: 9000
diff --git a/roles/compute-node-enable-maas/tasks/main.yml b/roles/compute-node-enable-maas/tasks/main.yml
new file mode 100644
index 0000000..9892aa1
--- /dev/null
+++ b/roles/compute-node-enable-maas/tasks/main.yml
@@ -0,0 +1,39 @@
+---
+# compute-node-enable-maas/tasks/main.yml
+
+- name: Fetch generated nodes.yaml file
+  fetch:
+    src: "{{ cord_profile_dir + '/' + item }}"
+    dest: "/tmp/{{ item }}"
+    flat: yes
+    fail_on_missing: yes
+  with_items:
+    - openstack.yaml
+    - vtn-service.yaml
+    - openstack-compute.yaml
+    - openstack-compute-vtn.yaml
+
+- name: Load TOSCA to add OpenStack compute nodes, over REST
+  xostosca:
+    url: "http://xos.{{ site_suffix }}:{{ xos_ui_port }}/api/utility/tosca/run/"
+    user: "{{ xos_admin_user }}"
+    password:  "{{ xos_admin_pass }}"
+    recipe: "{{ lookup('file', '/tmp/' + item ) }}"
+  with_items:
+    - openstack.yaml
+    - openstack-compute.yaml
+
+- name: Pause to work around race in VTN or ONOS synchronizers
+  pause:
+    seconds: 20
+
+- name: Load TOSCA to enable VTN on OpenStack compute nodes, over REST
+  xostosca:
+    url: "http://xos.{{ site_suffix }}:{{ xos_ui_port }}/api/utility/tosca/run/"
+    user: "{{ xos_admin_user }}"
+    password:  "{{ xos_admin_pass }}"
+    recipe: "{{ lookup('file', '/tmp/' + item ) }}"
+  with_items:
+    - vtn-service.yaml
+    - openstack-compute-vtn.yaml
+
diff --git a/roles/compute-node-enable/defaults/main.yml b/roles/compute-node-enable/defaults/main.yml
new file mode 100644
index 0000000..a3a1e7c
--- /dev/null
+++ b/roles/compute-node-enable/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+# compute-node-enable/defaults/main.yml
+
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
diff --git a/roles/compute-node-enable/tasks/main.yml b/roles/compute-node-enable/tasks/main.yml
new file mode 100644
index 0000000..5f3557c
--- /dev/null
+++ b/roles/compute-node-enable/tasks/main.yml
@@ -0,0 +1,17 @@
+---
+# compute-node-enable/tasks/main.yml
+
+- name: Load TOSCA to add OpenStack compute nodes
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} {{ cord_profile_dir }}/openstack-compute.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Pause to work around race in VTN or ONOS synchronizers
+  pause:
+    seconds: 20
+
+- name: Load TOSCA to enable VTN on OpenStack compute nodes
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} {{ cord_profile_dir }}/openstack-compute-vtn.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
diff --git a/roles/cord-profile/defaults/main.yml b/roles/cord-profile/defaults/main.yml
new file mode 100644
index 0000000..a5d2ce8
--- /dev/null
+++ b/roles/cord-profile/defaults/main.yml
@@ -0,0 +1,85 @@
+---
+# cord-profile/defaults/main.yml
+
+cord_dir: "{{ ansible_user_dir + '/cord' }}"
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
+# used in xos.yaml.j2, if True, other synchronizer container will not be started
+frontend_only: False
+
+# Set to True if you want to copy the admin-openrc.sh openstack config file
+use_openstack: True
+
+# set to True to create the xos_redis container in the bootstrap context
+use_redis: True
+
+use_vtn: True
+
+xos_docker_networks:
+  - "xos"
+
+xos_docker_volumes: []
+
+xos_bootstrap_ui_port: 9001
+xos_ui_port: 9000
+
+xos_users: []
+
+xos_libraries:
+  - "ng-xos-lib"
+
+xos_services: []
+xos_service_sshkeys: []
+
+xos_images: []
+
+xos_tosca_config_templates: []
+
+xos_other_templates: []
+
+# GUI branding, used in xos_common_config.j2
+disable_minidashboard: "True"
+gui_branding_name: "OpenCloud"
+gui_branding_icon: "/static/logo.png"
+gui_branding_favicon: "/static/favicon.png"
+gui_branding_bg: "/static/bg.jpg"
+gui_service_view_class: False
+
+# used in deployment.yaml.j2
+xos_admin_user: "xosadmin@opencord.org"
+xos_admin_pass: "{{ lookup('password', 'credentials/xosadmin@opencord.org chars=ascii_letters,digits') }}"
+xos_admin_first: XOS
+xos_admin_last: Admin
+
+site_name: sitename
+site_humanname: "Site HumanName"
+
+deployment_type: deploymenttype
+
+deployment_flavors:
+  - m1.small
+  - m1.medium
+  - m1.large
+  - m1.xlarge
+
+# used in management-net.yaml.j2
+management_network_cidr: 172.27.0.0/24
+
+use_management_hosts: False
+management_hosts_net_cidr: 10.1.0.1/24
+management_hosts_net_range_xos_low: "10.1.0.128"
+management_hosts_net_range_xos_high: "10.1.0.254"
+
+# used in fabric.yaml.j2
+use_fabric: False
+fabric_network_cfg_json: "/opt/cord_profile/fabric-network-cfg.json"
+
+# used in volt-devices.yaml.j2
+volt_devices:
+  - name: voltdev
+    openflow_id: "of:1000000000000001"
+    access_devices: "2 222, 3 223, 4 224"
+    agent_mac: "AA:BB:CC:DD:EE:FF"
+    agent_port_mappings: "of:0000000000000002/2 DE:AD:BE:EF:BA:11, of:0000000000000002/3 BE:EF:DE:AD:BE:EF"
+
+cord_app_version: "1.2-SNAPSHOT"
diff --git a/roles/cord-profile/files/disable-onboarding.yaml b/roles/cord-profile/files/disable-onboarding.yaml
new file mode 100644
index 0000000..f597d70
--- /dev/null
+++ b/roles/cord-profile/files/disable-onboarding.yaml
@@ -0,0 +1,16 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Disable builds for the Onboarding synchronizer
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    xos:
+      type: tosca.nodes.XOS
+      properties:
+        no-create: true
+        no-delete: true
+        enable_build: false
+
diff --git a/roles/cord-profile/files/enable-onboarding.yaml b/roles/cord-profile/files/enable-onboarding.yaml
new file mode 100644
index 0000000..73545e8
--- /dev/null
+++ b/roles/cord-profile/files/enable-onboarding.yaml
@@ -0,0 +1,16 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Enable builds for the Onboarding synchronizer
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    xos:
+      type: tosca.nodes.XOS
+      properties:
+        no-create: true
+        no-delete: true
+        enable_build: true
+
diff --git a/roles/cord-profile/files/fixtures.yaml b/roles/cord-profile/files/fixtures.yaml
new file mode 100644
index 0000000..00da082
--- /dev/null
+++ b/roles/cord-profile/files/fixtures.yaml
@@ -0,0 +1,156 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Some basic fixtures
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+    xos:
+      type: tosca.nodes.XOS
+
+# -----------------------------------------------------------------------------
+# Network Parameter Types
+# -----------------------------------------------------------------------------
+
+    s_tag:
+      type: tosca.nodes.NetworkParameterType
+
+    c_tag:
+      type: tosca.nodes.NetworkParameterType
+
+    next_hop:
+      type: tosca.nodes.NetworkParameterType
+
+    device:
+      type: tosca.nodes.NetworkParameterType
+
+    bridge:
+      type: tosca.nodes.NetworkParameterType
+
+    neutron_port_name:
+      type: tosca.nodes.NetworkParameterType
+
+# ----------------------------------------------------------------------------
+# Roles
+# ----------------------------------------------------------------------------
+
+    siterole#admin:
+      type: tosca.nodes.SiteRole
+
+    siterole#pi:
+      type: tosca.nodes.SiteRole
+
+    siterole#tech:
+      type: tosca.nodes.SiteRole
+
+    tenantrole#admin:
+      type: tosca.nodes.TenantRole
+
+    tenantrole#access:
+      type: tosca.nodes.TenantRole
+
+    deploymentrole#admin:
+      type: tosca.nodes.DeploymentRole
+
+    slicerole#admin:
+      type: tosca.nodes.SliceRole
+
+    slicerole#access:
+      type: tosca.nodes.SliceRole
+
+# -----------------------------------------------------------------------------
+# Flavors
+# -----------------------------------------------------------------------------
+
+    m1.small:
+      type: tosca.nodes.Flavor
+
+    m1.medium:
+      type: tosca.nodes.Flavor
+
+    m1.large:
+      type: tosca.nodes.Flavor
+
+    m1.xlarge:
+      type: tosca.nodes.Flavor
+
+# -----------------------------------------------------------------------------
+# Dashboard Views
+# -----------------------------------------------------------------------------
+
+# Temporary removed, waiting for a new Angular Base Implementation
+#    xsh:
+#      type: tosca.nodes.DashboardView
+#      properties:
+#          url: template:xsh
+
+    Customize:
+      type: tosca.nodes.DashboardView
+      properties:
+          url: template:xosDashboardManager
+          custom_icon: true
+
+    Diagnostic:
+      type: tosca.nodes.DashboardView
+      properties:
+          url: template:xosDiagnostic
+          custom_icon: true
+
+    Truckroll:
+      type: tosca.nodes.DashboardView
+      properties:
+          url: template:xosTruckroll
+          custom_icon: true
+
+    Monitoring:
+      type: tosca.nodes.DashboardView
+      properties:
+          url: template:xosCeilometerDashboard
+
+    Subscribers:
+      type: tosca.nodes.DashboardView
+      properties:
+          url: template:xosSubscribers
+
+    Tenant:
+      type: tosca.nodes.DashboardView
+      properties:
+          url: template:xosTenant
+
+    Developer:
+      type: tosca.nodes.DashboardView
+      properties:
+          url: template:xosDeveloper
+
+    Services Grid:
+      type: tosca.nodes.DashboardView
+      properties:
+          url: template:xosServiceGrid
+
+# -----------------------------------------------------------------------------
+# Network Templates
+# -----------------------------------------------------------------------------
+
+    Private:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+          visibility: private
+          translation: none
+
+    Public shared IPv4:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+          visibility: private
+          translation: NAT
+          shared_network_name: nat-net
+
+    Public dedicated IPv4:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+          visibility: public
+          translation: none
+          shared_network_name: ext-net
+
diff --git a/roles/cord-profile/tasks/main.yml b/roles/cord-profile/tasks/main.yml
new file mode 100644
index 0000000..d0f9874
--- /dev/null
+++ b/roles/cord-profile/tasks/main.yml
@@ -0,0 +1,95 @@
+---
+# cord-profile/tasks/main.yml
+# Constructs a CORD service profile directory and configuration files
+
+- name: Create cord_profile directory
+  become: yes
+  file:
+    path: "{{ cord_profile_dir }}"
+    state: directory
+    mode: 0755
+    owner: "{{ ansible_user_id }}"
+    group: "{{ ansible_user_gid }}"
+
+- name: Create subdirectories inside cord_profile directory
+  file:
+    path: "{{ cord_profile_dir }}/{{ item }}"
+    state: directory
+    mode: 0755
+  with_items:
+    - key_import
+    - onboarding-docker-compose
+    - images
+
+- name: Copy ssh keys to key_import directory
+  copy:
+    remote_src: True # file is local to the remote machine
+    src: "{{ item.source_path | expanduser }}"
+    dest: "{{ cord_profile_dir }}/key_import/{{ item.name }}"
+    mode: 0600
+  with_items: "{{ xos_service_sshkeys }}"
+
+- name: Copy node_key to cord_profile directory
+  copy:
+    remote_src: True # file is local to the remote machine
+    src: "{{ ansible_user_dir }}/node_key"
+    dest: "{{ cord_profile_dir }}/node_key"
+    mode: 0600
+
+- name: Copy over core api key
+  copy:
+    src: "{{ playbook_dir }}/pki/intermediate_ca/private/xos-core.{{ site_suffix }}_key.pem"
+    dest: "{{ cord_profile_dir }}/core_api_key.pem"
+    mode: 0600
+
+- name: Copy over core api cert
+  copy:
+    src: "{{ playbook_dir }}/pki/intermediate_ca/certs/xos-core.{{ site_suffix }}_cert_chain.pem"
+    dest: "{{ cord_profile_dir }}/core_api_cert.pem"
+
+- name: Download Glance VM images
+  get_url:
+    url: "{{ item.url }}"
+    checksum: "{{ item.checksum }}"
+    dest: "{{ cord_profile_dir }}/images/{{ item.name }}.qcow2"
+  with_items: "{{ xos_images }}"
+
+- name: Copy over commonly used and utility TOSCA files
+  copy:
+    src: "{{ item }}"
+    dest: "{{ cord_profile_dir }}/{{ item }}"
+  with_items:
+    - fixtures.yaml
+    - enable-onboarding.yaml
+    - disable-onboarding.yaml
+
+- name: Create templated XOS configuration files
+  template:
+    src: "{{ item }}.j2"
+    dest: "{{ cord_profile_dir }}/{{ item }}"
+    mode: 0644
+  with_items:
+    - xos_common_config
+    - deployment.yaml
+    - xos.yaml
+    - xos-bootstrap-docker-compose.yaml
+
+- name: Create profile specific templated TOSCA config files
+  template:
+    src: "{{ item }}.j2"
+    dest: "{{ cord_profile_dir }}/{{ item }}"
+  with_items: "{{ xos_tosca_config_templates }}"
+
+- name: Create profile specific templated non-TOSCA files
+  template:
+    src: "{{ item }}.j2"
+    dest: "{{ cord_profile_dir }}/{{ item }}"
+  with_items: "{{ xos_other_templates }}"
+
+- name: Copy admin_openrc.sh
+  when: use_openstack
+  copy:
+    remote_src: True
+    src: "{{ ansible_user_dir }}/admin-openrc.sh"
+    dest: "{{ cord_profile_dir }}/admin-openrc.sh"
+
diff --git a/roles/cord-profile/templates/cdn-content.yaml.j2 b/roles/cord-profile/templates/cdn-content.yaml.j2
new file mode 100644
index 0000000..7b6ef00
--- /dev/null
+++ b/roles/cord-profile/templates/cdn-content.yaml.j2
@@ -0,0 +1,224 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Hypercache CDN Content
+
+imports:
+   - custom_types/xos.yaml
+   - custom_types/cdn.yaml
+
+topology_template:
+  node_templates:
+    HyperCache:
+      type: tosca.nodes.CDNService
+      properties:
+          # HyperCache service must already exist before running this recipe
+          no-create: true
+          no-delete: true
+          no-update: true
+
+    # Setup the CDN Service Provider
+
+    main_service_provider:
+        type: tosca.nodes.ServiceProvider
+        requirements:
+           - hpc_service:
+                 node: HyperCache
+                 relationship: tosca.relationships.MemberOfService
+
+    # Wall Street Journal Content Provider
+
+    wsj_content:
+        type: tosca.nodes.ContentProvider
+        requirements:
+            - service_provider:
+                  node: main_service_provider
+                  relationship: tosca.relationships.MemberOfServiceProvider
+
+    www.wsj.com:
+        type: tosca.nodes.CDNPrefix
+        requirements:
+             - content_provider:
+                   node: wsj_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+             - default_origin_server:
+                   node: http_www.wsj.com
+                   relationship: tosca.relationships.DefaultOriginServer
+
+    si.wsj.net:
+        type: tosca.nodes.CDNPrefix
+        requirements:
+             - content_provider:
+                   node: wsj_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+             - default_origin_server:
+                   node: http_si.wsj.net
+                   relationship: tosca.relationships.DefaultOriginServer
+
+    s.wsj.net:
+        type: tosca.nodes.CDNPrefix
+        requirements:
+             - content_provider:
+                   node: wsj_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+             - default_origin_server:
+                   node: http_s.wsj.net
+                   relationship: tosca.relationships.DefaultOriginServer
+
+    ore.wsj.net:
+        type: tosca.nodes.CDNPrefix
+        requirements:
+             - content_provider:
+                   node: wsj_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+             - default_origin_server:
+                   node: http_ore.wsj.net
+                   relationship: tosca.relationships.DefaultOriginServer
+
+    http_www.wsj.com:
+        type: tosca.nodes.OriginServer
+        requirements:
+             - content_provider:
+                   node: wsj_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+
+    http_si.wsj.net:
+        type: tosca.nodes.OriginServer
+        requirements:
+             - content_provider:
+                   node: wsj_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+
+    http_s.wsj.net:
+        type: tosca.nodes.OriginServer
+        requirements:
+             - content_provider:
+                   node: wsj_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+
+    http_ore.wsj.net:
+        type: tosca.nodes.OriginServer
+        requirements:
+             - content_provider:
+                   node: wsj_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+
+    # ON.Lab content provider
+
+    on_lab_content:
+        type: tosca.nodes.ContentProvider
+        requirements:
+            - service_provider:
+                  node: main_service_provider
+                  relationship: tosca.relationships.MemberOfServiceProvider
+
+    # Create CDN prefix onlab.vicci.org
+    onlab.vicci.org:
+        type: tosca.nodes.CDNPrefix
+        requirements:
+             - content_provider:
+                   node: on_lab_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+
+    http_onos-videos.s3.amazonaws.com:
+        type: tosca.nodes.OriginServer
+        requirements:
+             - content_provider:
+                   node: on_lab_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+
+    # Create origin server s3-us-west-1.amazonaws.com
+    http_s3-us-west-1.amazonaws.com:
+        type: tosca.nodes.OriginServer
+        requirements:
+             - content_provider:
+                   node: on_lab_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+
+    # Create origin server s3.amazonaws.com
+    http_s3.amazonaws.com:
+        type: tosca.nodes.OriginServer
+        requirements:
+             - content_provider:
+                   node: on_lab_content
+                   relationship: tosca.relationships.MemberOfContentProvider
+
+    # Test Content Provider
+
+    testcp2:
+        type: tosca.nodes.ContentProvider
+        requirements:
+            - service_provider:
+                  node: main_service_provider
+                  relationship: tosca.relationships.MemberOfServiceProvider
+
+    http_www.cs.arizona.edu:
+        type: tosca.nodes.OriginServer
+        requirements:
+             - content_provider:
+                   node: testcp2
+                   relationship: tosca.relationships.MemberOfContentProvider
+
+    test-cdn.opencloud.us:
+        type: tosca.nodes.CDNPrefix
+        requirements:
+             - content_provider:
+                   node: testcp2
+                   relationship: tosca.relationships.MemberOfContentProvider
+
+             - default_origin_server:
+                   node: http_www.cs.arizona.edu
+                   relationship: tosca.relationships.DefaultOriginServer
+
+    # Health Checks
+
+    healthcheck_dns_onlab.vicci.org:
+        type: tosca.nodes.HpcHealthCheck
+        requirements:
+           - hpc_service:
+                 node: HyperCache
+                 relationship: tosca.relationships.MemberOfService
+        properties:
+           kind: dns
+           resource_name: onlab.vicci.org
+
+    healthcheck_dns_test-cdn.opencloud.us:
+        type: tosca.nodes.HpcHealthCheck
+        requirements:
+           - hpc_service:
+                 node: HyperCache
+                 relationship: tosca.relationships.MemberOfService
+        properties:
+           kind: dns
+           resource_name: test-cdn.opencloud.us
+
+    healthcheck_http_test-cdn-index:
+        type: tosca.nodes.HpcHealthCheck
+        requirements:
+           - hpc_service:
+                 node: HyperCache
+                 relationship: tosca.relationships.MemberOfService
+        properties:
+           kind: http
+           resource_name: test-cdn.opencloud.us:/
+           result_contains: Lowenthal
+
+    healthcheck_http_onlab_onos_image:
+        type: tosca.nodes.HpcHealthCheck
+        requirements:
+           - hpc_service:
+                 node: HyperCache
+                 relationship: tosca.relationships.MemberOfService
+        properties:
+           kind: http
+           resource_name: onlab.vicci.org:/onos/vm/onos-tutorial-1.1.0r220-ovf.zip
+
+    healthcheck_http_onlab_mininet_image:
+        type: tosca.nodes.HpcHealthCheck
+        requirements:
+           - hpc_service:
+                 node: HyperCache
+                 relationship: tosca.relationships.MemberOfService
+        properties:
+           kind: http
+           resource_name: onlab.vicci.org:/mininet-vm/mininet-2.1.0-130919-ubuntu-13.04-server-amd64-ovf.zip
+
diff --git a/roles/xos-install/templates/cord-services.yaml.j2 b/roles/cord-profile/templates/cord-services.yaml.j2
similarity index 95%
rename from roles/xos-install/templates/cord-services.yaml.j2
rename to roles/cord-profile/templates/cord-services.yaml.j2
index b182f04..0c3c2fd 100644
--- a/roles/xos-install/templates/cord-services.yaml.j2
+++ b/roles/cord-profile/templates/cord-services.yaml.j2
@@ -23,12 +23,14 @@
         no-delete: true
         no-update: true
 
+{% if use_management_hosts %}
     management_hosts:
       type: tosca.nodes.network.Network.XOS
       properties:
         no-create: true
         no-delete: true
         no-update: true
+{% endif %}
 
 # ONOS_CORD, fully created in vtn.yaml
     service#ONOS_CORD:
@@ -167,6 +169,11 @@
         - management:
             node: management
             relationship: tosca.relationships.ConnectsToNetwork
+{% if use_management_hosts %}
+        - management_hosts:
+            node: management_hosts
+            relationship: tosca.relationships.ConnectsToNetwork
+{% endif %}
         - image:
             node: image#vsg-1.1
             relationship: tosca.relationships.DefaultImage
diff --git a/roles/xos-install/templates/deployment.yaml.j2 b/roles/cord-profile/templates/deployment.yaml.j2
similarity index 77%
rename from roles/xos-install/templates/deployment.yaml.j2
rename to roles/cord-profile/templates/deployment.yaml.j2
index 2d5dfd1..0987b7c 100644
--- a/roles/xos-install/templates/deployment.yaml.j2
+++ b/roles/cord-profile/templates/deployment.yaml.j2
@@ -51,6 +51,23 @@
               relationship: tosca.relationships.SupportsDeployment
 
 # XOS Users
+# Default admin user account
+    {{ xos_admin_user }}:
+      type: tosca.nodes.User
+      properties:
+        password: {{ xos_admin_pass }}
+        firstname: {{ xos_admin_first }}
+        lastname: {{ xos_admin_last }}
+        is_admin: True
+      requirements:
+        - site:
+            node: {{ site_name }}
+            relationship: tosca.relationships.MemberOfSite
+        - tenant_dashboard:
+              node: Tenant
+              relationship: tosca.relationships.UsesDashboard
+
+# All other users
 {% for user in xos_users %}
     {{ user.email }}:
       type: tosca.nodes.User
diff --git a/roles/cord-profile/templates/fabric-network-cfg.json.j2 b/roles/cord-profile/templates/fabric-network-cfg.json.j2
new file mode 100644
index 0000000..8609ef8
--- /dev/null
+++ b/roles/cord-profile/templates/fabric-network-cfg.json.j2
@@ -0,0 +1,100 @@
+{
+    "ports" : {
+        "of:0000000000000001/1" : {
+            "interfaces" : [
+                {
+                    "ips" : [ "10.6.1.0/24" ]
+                }
+            ]
+        },
+        "of:0000000000000001/2" : {
+            "interfaces" : [
+                {
+                    "ips" : [ "10.6.1.0/24" ]
+                }
+            ]
+        },
+        "of:0000000000000002/3" : {
+            "interfaces" : [
+                {
+                    "ips" : [ "10.6.2.0/24" ]
+                }
+            ]
+        },
+        "of:0000000000000002/4" : {
+            "interfaces" : [
+                {
+                    "ips" : [ "10.6.2.0/24" ]
+                }
+            ]
+        }
+    },
+    "devices" : {
+        "of:0000000000000001" : {
+            "segmentrouting" : {
+                "name" : "Leaf-R1",
+                "nodeSid" : 101,
+                "routerIp" : "10.6.1.254",
+                "routerMac" : "00:00:00:00:01:80",
+                "isEdgeRouter" : true,
+                "adjacencySids" : []
+            }
+        },
+        "of:0000000000000002" : {
+            "segmentrouting" : {
+                "name" : "Leaf-R2",
+                "nodeSid" : 102,
+                "routerIp" : "10.6.2.254",
+                "routerMac" : "00:00:00:00:02:80",
+                "isEdgeRouter" : true,
+                "adjacencySids" : []
+            }
+        },
+        "of:0000000000000191" : {
+            "segmentrouting" : {
+                "name" : "Spine-R1",
+                "nodeSid" : 103,
+                "routerIp" : "192.168.0.11",
+                "routerMac" : "00:00:01:00:11:80",
+                "isEdgeRouter" : false,
+                "adjacencySids" : []
+            }
+        },
+        "of:0000000000000192" : {
+            "segmentrouting" : {
+                "name" : "Spine-R2",
+                "nodeSid" : 104,
+                "routerIp" : "192.168.0.22",
+                "routerMac" : "00:00:01:00:22:80",
+                "isEdgeRouter" : false,
+                "adjacencySids" : []
+            }
+        }
+    },
+    "hosts" : {
+        "00:00:00:00:00:01/-1" : {
+            "basic": {
+                "ips": ["10.6.1.1"],
+                "location": "of:0000000000000001/1"
+            }
+        },
+        "00:00:00:00:00:02/-1" : {
+            "basic": {
+                "ips": ["10.6.1.2"],
+                "location": "of:0000000000000001/2"
+            }
+        },
+        "00:00:00:00:00:03/-1" : {
+            "basic": {
+                "ips": ["10.6.2.1"],
+                "location": "of:0000000000000002/3"
+            }
+        },
+        "00:00:00:00:00:04/-1" : {
+            "basic": {
+                "ips": ["10.6.2.2"],
+                "location": "of:0000000000000002/4"
+            }
+        }
+    }
+}
diff --git a/roles/xos-install/templates/fabric.yaml.j2 b/roles/cord-profile/templates/fabric-service.yaml.j2
similarity index 65%
rename from roles/xos-install/templates/fabric.yaml.j2
rename to roles/cord-profile/templates/fabric-service.yaml.j2
index 664505f..36ad25a 100644
--- a/roles/xos-install/templates/fabric.yaml.j2
+++ b/roles/cord-profile/templates/fabric-service.yaml.j2
@@ -3,8 +3,7 @@
 imports:
    - custom_types/xos.yaml
 
-description: fabric configuration generated by platform-install
-
+description: fabric services, generated by platform-install
 
 topology_template:
   node_templates:
@@ -20,7 +19,7 @@
           replaces: service_ONOS_Fabric
           rest_onos/v1/network/configuration/: { get_artifact: [ SELF, fabric_network_cfg_json, LOCAL_FILE ] }
       artifacts:
-          fabric_network_cfg_json: /root/setup/network-cfg-quickstart.json
+          fabric_network_cfg_json: {{ fabric_network_cfg_json }}
 
     service#fabric:
       type: tosca.nodes.FabricService
@@ -40,23 +39,3 @@
       properties:
           dependencies: org.onosproject.drivers, org.onosproject.openflow-base, org.onosproject.netcfghostprovider, org.onosproject.netcfglinksprovider, org.onosproject.segmentrouting, org.onosproject.vrouter, org.onosproject.hostprovider
 
-{% for node in groups["compute"] %}
-    {{ node }}:
-      type: tosca.nodes.Node
-
-    # Fabric location field for node $NODE
-    {{ node }}_location_tag:
-      type: tosca.nodes.Tag
-      properties:
-          name: location
-          value: of:0000000000000001/1
-      requirements:
-          - target:
-              node: {{ node }}
-              relationship: tosca.relationships.TagsObject
-          - service:
-              node: service#ONOS_Fabric
-              relationship: tosca.relationships.MemberOfService
-
-{% endfor %}
-
diff --git a/roles/xos-install/templates/management-net.yaml.j2 b/roles/cord-profile/templates/management-net.yaml.j2
similarity index 65%
rename from roles/xos-install/templates/management-net.yaml.j2
rename to roles/cord-profile/templates/management-net.yaml.j2
index 79ea589..781dbf3 100644
--- a/roles/xos-install/templates/management-net.yaml.j2
+++ b/roles/cord-profile/templates/management-net.yaml.j2
@@ -20,13 +20,6 @@
         translation: none
         vtn_kind: MANAGEMENT_LOCAL
 
-    management_hosts_template:
-      type: tosca.nodes.NetworkTemplate
-      properties:
-          visibility: private
-          translation: none
-          vtn_kind: MANAGEMENT_HOST
-
     management:
       type: tosca.nodes.network.Network
       properties:
@@ -40,6 +33,30 @@
             node: {{ site_name }}_management
             relationship: tosca.relationships.MemberOfSlice
 
+{% if use_management_hosts %}
+    management_hosts_template:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+        visibility: private
+        translation: none
+        vtn_kind: MANAGEMENT_HOST
+
+    management_hosts:
+      type: tosca.nodes.network.Network
+      properties:
+        ip_version: 4
+        cidr: {{ management_hosts_net_cidr }}
+        start_ip: {{ management_hosts_net_range_xos_low }}
+        end_ip: {{ management_hosts_net_range_xos_high }}
+      requirements:
+        - network_template:
+            node: management_hosts_template
+            relationship: tosca.relationships.UsesNetworkTemplate
+        - owner:
+            node: {{ site_name }}_management
+            relationship: tosca.relationships.MemberOfSlice
+{% endif %}
+
     {{ site_name }}_management:
       description: This slice exists solely to own the management network
       type: tosca.nodes.Slice
diff --git a/roles/cord-profile/templates/mock-mcord.yaml.j2 b/roles/cord-profile/templates/mock-mcord.yaml.j2
new file mode 100644
index 0000000..060f829
--- /dev/null
+++ b/roles/cord-profile/templates/mock-mcord.yaml.j2
@@ -0,0 +1,319 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Setup CORD-related services
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    # M-CORD Services
+    
+    # RAN
+    vBBU:
+      type: tosca.nodes.Service
+      properties:
+          view_url: /mcord/?service=vBBU
+          kind: RAN
+
+    eSON:
+      type: tosca.nodes.Service
+      properties:
+          view_url: http://www.google.com
+          kind: RAN
+
+    # EPC
+    vMME:
+      type: tosca.nodes.Service
+      properties:
+          view_url: /mcord/?service=vMME
+          kind: EPC
+
+    vSGW:
+      type: tosca.nodes.Service
+      properties:
+          view_url: /mcord/?service=vSGW
+          kind: EPC
+
+    vPGW:
+      type: tosca.nodes.Service
+      properties:
+          view_url: /mcord/?service=vPGW
+          kind: EPC
+
+    # EDGE
+    Cache:
+      type: tosca.nodes.Service
+      properties:
+          view_url: /mcord/?service=Cache
+          icon_url: /static/mCordServices/service_cache.png
+          kind: EDGE
+
+    Firewall:
+      type: tosca.nodes.Service
+      properties:
+          view_url: /mcord/?service=Firewall
+          icon_url: /static/mCordServices/service_firewall.png
+          kind: EDGE
+
+    Video Optimization:
+      type: tosca.nodes.Service
+      properties:
+          view_url: /mcord/?service=Video%20Optimization
+          icon_url: /static/mCordServices/service_video.png
+          kind: EDGE
+          
+    # Images
+    trusty-server-multi-nic:
+      type: tosca.nodes.Image
+      properties:
+         disk_format: QCOW2
+         container_format: BARE
+
+    # Deployments
+    StanfordDeployment:
+      type: tosca.nodes.Deployment
+      properties:
+          flavors: m1.large, m1.medium, m1.small
+      requirements:
+          - image:
+              node: trusty-server-multi-nic
+              relationship: tosca.relationships.SupportsImage
+
+    # Site
+    stanford:
+      type: tosca.nodes.Site
+      properties:
+          display_name: Stanford University
+          site_url: https://www.stanford.edu/
+      requirements:
+          - deployment:
+               node: StanfordDeployment
+               relationship: tosca.relationships.MemberOfDeployment
+          - controller:
+               node: CloudLab
+               relationship: tosca.relationships.UsesController
+
+
+    # Nodes
+    node1.stanford.edu:
+      type: tosca.nodes.Node
+      requirements:
+        - site:
+            node: stanford
+            relationship: tosca.relationships.MemberOfSite
+        - deployment:
+            node: StanfordDeployment
+            relationship: tosca.relationships.MemberOfDeployment
+
+    # Slices
+    stanford_slice:
+      description: Slice that contains sample instances
+      type: tosca.nodes.Slice
+      requirements:
+          - site:
+              node: stanford
+              relationship: tosca.relationships.MemberOfSite
+
+    # Instances
+    BBU_service_instance1:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: ubuntu
+            version: 14.04
+      requirements:
+          - slice:
+                node: stanford_slice
+                relationship: tosca.relationships.MemberOfSlice
+
+    BBU_service_instance2:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: ubuntu
+            version: 14.04
+      requirements:
+          - slice:
+                node: stanford_slice
+                relationship: tosca.relationships.MemberOfSlice
+
+    MME_service_instance1:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: ubuntu
+            version: 14.04
+      requirements:
+          - slice:
+                node: stanford_slice
+                relationship: tosca.relationships.MemberOfSlice
+
+    SGW_service_instance1:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: ubuntu
+            version: 14.04
+      requirements:
+          - slice:
+                node: stanford_slice
+                relationship: tosca.relationships.MemberOfSlice
+
+    PGW_service_instance1:
+      type: tosca.nodes.Compute
+      capabilities:
+        # Host container properties
+        host:
+         properties:
+           num_cpus: 1
+           disk_size: 10 GB
+           mem_size: 4 MB
+        # Guest Operating System properties
+        os:
+          properties:
+            # host Operating System image properties
+            architecture: x86_64
+            type: linux
+            distribution: ubuntu
+            version: 14.04
+      requirements:
+          - slice:
+                node: stanford_slice
+                relationship: tosca.relationships.MemberOfSlice
+
+    # Let's add a user who can be administrator of the household
+    johndoe@stanford.us:
+      type: tosca.nodes.User
+      properties:
+          password: letmein
+          firstname: john
+          lastname: doe
+      requirements:
+          - site:
+              node: stanford
+              relationship: tosca.relationships.MemberOfSite
+
+    # A subscriber
+    Stanford:
+       type: tosca.nodes.CORDSubscriber
+       properties:
+           service_specific_id: 123
+           firewall_enable: false
+           cdn_enable: false
+           url_filter_enable: false
+           url_filter_level: R
+       requirements:
+          - house_admin:
+              node: johndoe@stanford.us
+              relationship: tosca.relationships.AdminPrivilege
+
+    Barbera Lapinski:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 01:02:03:04:05:06
+           level: PG_13
+       requirements:
+           - household:
+               node: Stanford
+               relationship: tosca.relationships.SubscriberDevice
+
+    Norbert Shumway:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 90:E2:BA:82:F9:75
+           level: PG_13
+       requirements:
+           - household:
+               node: Stanford
+               relationship: tosca.relationships.SubscriberDevice
+
+    Fay Muldoon:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 68:5B:35:9D:91:D5
+           level: PG_13
+       requirements:
+           - household:
+               node: Stanford
+               relationship: tosca.relationships.SubscriberDevice
+
+    Janene Earnest:
+       type: tosca.nodes.CORDUser
+       properties:
+           mac: 34:36:3B:C9:B6:A6
+           level: PG_13
+       requirements:
+           - household:
+               node: Stanford
+               relationship: tosca.relationships.SubscriberDevice
+
+
+    Topology:
+      type: tosca.nodes.DashboardView
+      properties:
+          url: template:xosMcordTopology
+
+    Ceilometer:
+      type: tosca.nodes.DashboardView
+      properties:
+          url: template:xosCeilometerDashboard
+
+    padmin@vicci.org:
+      type: tosca.nodes.User
+      properties:
+          firstname: XOS
+          lastname: admin
+          is_admin: true
+      requirements:
+          - mcord_dashboard:
+              node: Topology
+              relationship: tosca.relationships.UsesDashboard
+          - ceilometer_dashboard:
+              node: Ceilometer
+              relationship: tosca.relationships.UsesDashboard
+
diff --git a/roles/cord-profile/templates/mock-onos.yaml.j2 b/roles/cord-profile/templates/mock-onos.yaml.j2
new file mode 100644
index 0000000..1f733f9
--- /dev/null
+++ b/roles/cord-profile/templates/mock-onos.yaml.j2
@@ -0,0 +1,16 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+imports:
+   - custom_types/xos.yaml
+
+description: enough onos config to make the mock work
+
+topology_template:
+  node_templates:
+
+    service#ONOS_Fabric:
+      type: tosca.nodes.ONOSService
+
+    service#ONOS_CORD:
+      type: tosca.nodes.ONOSService
+
diff --git a/roles/xos-install/templates/openstack.yaml.j2 b/roles/cord-profile/templates/openstack.yaml.j2
similarity index 97%
rename from roles/xos-install/templates/openstack.yaml.j2
rename to roles/cord-profile/templates/openstack.yaml.j2
index 65d2338..07882f1 100644
--- a/roles/xos-install/templates/openstack.yaml.j2
+++ b/roles/cord-profile/templates/openstack.yaml.j2
@@ -51,7 +51,7 @@
           admin_tenant: { get_script_env: [ SELF, adminrc, OS_TENANT_NAME, LOCAL_FILE] }
           domain: Default
       artifacts:
-          adminrc: /root/setup/admin-openrc.sh
+          adminrc: /opt/cord_profile/admin-openrc.sh
 
 # Site - adds openstack controller to site defined in deployment.yaml
     {{ site_name }}:
diff --git a/roles/xos-install/templates/public-net.yaml.j2 b/roles/cord-profile/templates/public-net.yaml.j2
similarity index 99%
rename from roles/xos-install/templates/public-net.yaml.j2
rename to roles/cord-profile/templates/public-net.yaml.j2
index 26573ed..cf111ca 100644
--- a/roles/xos-install/templates/public-net.yaml.j2
+++ b/roles/cord-profile/templates/public-net.yaml.j2
@@ -12,11 +12,13 @@
     {{ site_name }}:
       type: tosca.nodes.Site
 
+
 # vrouter service, fully created in cord-service.yaml
     service#vrouter:
       type: tosca.nodes.VRouterService
 
 # public network
+
     public_template:
       type: tosca.nodes.NetworkTemplate
       properties:
diff --git a/roles/cord-profile/templates/sample.yaml.j2 b/roles/cord-profile/templates/sample.yaml.j2
new file mode 100644
index 0000000..6a3324c
--- /dev/null
+++ b/roles/cord-profile/templates/sample.yaml.j2
@@ -0,0 +1,92 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+    * Some sample data to populate the demo frontend
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    trusty-server-multi-nic:
+      type: tosca.nodes.Image
+      properties:
+         disk_format: QCOW2
+         container_format: BARE
+
+    {{ deployment_type }}:
+      type: tosca.nodes.Deployment
+      properties:
+          flavors: m1.large, m1.medium, m1.small
+      requirements:
+          - image:
+              node: trusty-server-multi-nic
+              relationship: tosca.relationships.SupportsImage
+
+    CloudLab:
+      type: tosca.nodes.Controller
+      requirements:
+          - deployment:
+              node: {{ deployment_type }}
+              relationship: tosca.relationships.ControllerDeployment
+      properties:
+          backend_type: OpenStack
+          version: Juno
+          auth_url: http://sample/v2
+          admin_user: admin
+          admin_password: adminpassword
+          admin_tenant: admin
+          domain: Default
+
+    {{ site_name }}:
+      type: tosca.nodes.Site
+      properties:
+          display_name: {{ site_name }}
+          site_url: http://opencloud.us/
+      requirements:
+          - deployment:
+               node: {{ deployment_type }}
+               relationship: tosca.relationships.MemberOfDeployment
+          - controller:
+               node: CloudLab
+               relationship: tosca.relationships.UsesController
+
+    Public shared IPv4:
+      type: tosca.nodes.NetworkTemplate
+      properties:
+          visibility: private
+          translation: NAT
+
+    {{ xos_admin_user }}:
+      type: tosca.nodes.User
+      properties:
+        password: {{ xos_admin_pass }}
+        firstname: {{ xos_admin_first }}
+        lastname: {{ xos_admin_last }}
+        is_admin: True
+        is_active: True
+      requirements:
+        - site:
+            node: {{ site_name }}
+            relationship: tosca.relationships.MemberOfSite
+
+    node1.opencloud.us:
+      type: tosca.nodes.Node
+      requirements:
+        - site:
+            node: {{ site_name }}
+            relationship: tosca.relationships.MemberOfSite
+        - deployment:
+            node: {{ deployment_type }}
+            relationship: tosca.relationships.MemberOfDeployment
+
+    node2.opencloud.us:
+      type: tosca.nodes.Node
+      requirements:
+        - site:
+            node: {{ site_name }}
+            relationship: tosca.relationships.MemberOfSite
+        - deployment:
+            node: {{ deployment_type }}
+            relationship: tosca.relationships.MemberOfDeployment
+
diff --git a/roles/cord-profile/templates/services.yaml.j2 b/roles/cord-profile/templates/services.yaml.j2
new file mode 100644
index 0000000..055fa57
--- /dev/null
+++ b/roles/cord-profile/templates/services.yaml.j2
@@ -0,0 +1,67 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Created by platform-install/roles/cord-profile/templates/services.yaml.j2
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+    # CORD Services
+    service#vtr:
+      type: tosca.nodes.Service
+      properties:
+          view_url: /admin/vtr/vtrservice/$id$/
+          kind: vTR
+          replaces: service_vtr
+
+    service#volt:
+      type: tosca.nodes.VOLTService
+      requirements:
+          - vsg_tenant:
+              node: service#vsg
+              relationship: tosca.relationships.TenantOfService
+      properties:
+          view_url: /admin/cord/voltservice/$id$/
+          kind: vOLT
+          replaces: service_volt
+
+    addresses_vsg:
+      type: tosca.nodes.AddressPool
+      properties:
+          addresses: 10.168.0.0/24
+          gateway_ip: 10.168.0.1
+          gateway_mac: 02:42:0a:a8:00:01
+
+    addresses_exampleservice-public:
+      type: tosca.nodes.AddressPool
+      properties:
+          addresses: 10.168.1.0/24
+          gateway_ip: 10.168.1.1
+          gateway_mac: 02:42:0a:a8:00:01
+
+    service#vsg:
+      type: tosca.nodes.VSGService
+      requirements:
+          - vrouter_tenant:
+              node: service#vrouter
+              relationship: tosca.relationships.TenantOfService
+      properties:
+          view_url: /admin/cord/vsgservice/$id$/
+          private_key_fn: /opt/xos/synchronizers/vcpe/vcpe_private_key
+          replaces: service_vsg
+
+    service#vrouter:
+      type: tosca.nodes.VRouterService
+      properties:
+          view_url: /admin/vrouter/vrouterservice/$id$/
+          replaces: service_vrouter
+      requirements:
+          - addresses_vsg:
+              node: addresses_vsg
+              relationship: tosca.relationships.ProvidesAddresses
+          - addresses_service1:
+              node: addresses_exampleservice-public
+              relationship: tosca.relationships.ProvidesAddresses
+
diff --git a/roles/cord-profile/templates/volt-devices.yaml.j2 b/roles/cord-profile/templates/volt-devices.yaml.j2
new file mode 100644
index 0000000..ec882c9
--- /dev/null
+++ b/roles/cord-profile/templates/volt-devices.yaml.j2
@@ -0,0 +1,46 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: created by platform-install/roles/cord-profile/templates/volt-devices.yaml.j2
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+# vOLT service defined in services.yaml
+    service#volt:
+      type: tosca.nodes.VOLTService
+      properties:
+          no-create: True
+          no-delete: True
+          no-update: True
+
+# vOLT devices
+{% for device in volt_devices %}
+    {{ device.name }}-{{ device.index | default(loop.index) }}:
+      type: tosca.nodes.VOLTDevice
+      properties:
+            driver: {{ device.driver | default("pmc-olt") }}
+            openflow_id: {{ device.openflow_id }}
+            access_devices: {{ device.access_devices }}
+      requirements:
+          - volt_service:
+              node: service#volt
+              relationship: tosca.relationships.MemberOfService
+          - access_agent:
+              node: {{ device.name }}-agent-{{ device.index | default(loop.index) }}
+              relationship: tosca.relationships.UsesAgent
+
+    {{ device.name }}-agent-{{ device.index | default(loop.index) }}:
+      type: tosca.nodes.AccessAgent
+      properties:
+          mac: {{ device.agent_mac }}
+          port_mappings: {{ device.agent_port_mappings }}
+      requirements:
+          - volt_service:
+              node: service#volt
+              relationship: tosca.relationships.MemberOfService
+
+{% endfor %}
+
diff --git a/roles/cord-profile/templates/vrouter.yaml.j2 b/roles/cord-profile/templates/vrouter.yaml.j2
new file mode 100644
index 0000000..cf53513
--- /dev/null
+++ b/roles/cord-profile/templates/vrouter.yaml.j2
@@ -0,0 +1,174 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Just enough Tosca to get the vSG slice running on the CORD POD
+
+imports:
+   - custom_types/xos.yaml
+   - custom_types/vrouter.yaml
+
+topology_template:
+  node_templates:
+
+    service#vrouter:
+      type: tosca.nodes.VRouterService
+      properties:
+          view_url: /admin/vrouter/
+          no-delete: true
+          no-create: true
+          rest_hostname: onos-fabric
+          rest_port: 8181
+          rest_user: onos
+          rest_pass: rocks
+    
+    device#of:00000000000000b1:
+      type: tosca.nodes.VRouterDevice
+      properties:
+        openflow_id: of:00000000000000b1
+        driver: softrouter
+        # config_key: basic
+      requirements:
+        - service#vrouter:
+            node: service#vrouter
+            relationship: tosca.relationships.MemberOfService
+
+    # Port 1
+    port#port1/1:
+      type: tosca.nodes.VRouterPort
+      properties:
+        openflow_id: of:00000000000000b1/1
+      requirements:
+        - device#of:00000000000000b1:
+            node: device#of:00000000000000b1
+            relationship: tosca.relationships.PortOfDevice
+        - service#vrouter:
+            node: service#vrouter
+            relationship: tosca.relationships.MemberOfService
+
+    interface#b1-1:
+      type: tosca.nodes.VRouterInterface
+      properties:
+        name: b1-1
+        mac: 00:00:00:00:00:01
+      requirements:
+        - port#port1/1:
+            node: port#port1/1
+            relationship: tosca.relationships.InterfaceOfPort
+
+    ips#10.0.1.2/24:
+      type: tosca.nodes.VRouterIp
+      properties:
+        ip: 10.0.1.2/24
+      requirements:
+        - interface#b1-1:
+            node: interface#b1-1
+            relationship: tosca.relationships.IpOfInterface
+
+    # Port 2
+    port#port1/2:
+      type: tosca.nodes.VRouterPort
+      properties:
+        openflow_id: of:00000000000000b1/2
+      requirements:
+        - device#of:00000000000000b1:
+            node: device#of:00000000000000b1
+            relationship: tosca.relationships.PortOfDevice
+        - service#vrouter:
+            node: service#vrouter
+            relationship: tosca.relationships.MemberOfService
+
+    interface#b1-2:
+      type: tosca.nodes.VRouterInterface
+      properties:
+        name: b1-2
+        mac: 00:00:00:00:00:01
+      requirements:
+        - port#port1/2:
+            node: port#port1/2
+            relationship: tosca.relationships.InterfaceOfPort
+
+    ips#10.0.2.2/24:
+      type: tosca.nodes.VRouterIp
+      properties:
+        ip: 10.0.2.2/24
+      requirements:
+        - interface#b1-1:
+            node: interface#b1-2
+            relationship: tosca.relationships.IpOfInterface
+
+    # Port 3
+    port#port1/3:
+      type: tosca.nodes.VRouterPort
+      properties:
+        openflow_id: of:00000000000000b1/3
+      requirements:
+        - device#of:00000000000000b1:
+            node: device#of:00000000000000b1
+            relationship: tosca.relationships.PortOfDevice
+        - service#vrouter:
+            node: service#vrouter
+            relationship: tosca.relationships.MemberOfService
+
+    interface#b1-3:
+      type: tosca.nodes.VRouterInterface
+      properties:
+        name: b1-3
+        mac: 00:00:00:00:00:01
+      requirements:
+        - port#port1/3:
+            node: port#port1/3
+            relationship: tosca.relationships.InterfaceOfPort
+
+    ips#10.0.3.2/24:
+      type: tosca.nodes.VRouterIp
+      properties:
+        ip: 10.0.3.2/24
+      requirements:
+        - interface#b1-1:
+            node: interface#b1-3
+            relationship: tosca.relationships.IpOfInterface
+
+    # Port 4
+    port#port1/4:
+      type: tosca.nodes.VRouterPort
+      properties:
+        openflow_id: of:00000000000000b1/4
+      requirements:
+        - device#of:00000000000000b1:
+            node: device#of:00000000000000b1
+            relationship: tosca.relationships.PortOfDevice
+        - service#vrouter:
+            node: service#vrouter
+            relationship: tosca.relationships.MemberOfService
+
+    interface#b1-4:
+      type: tosca.nodes.VRouterInterface
+      properties:
+        name: b1-4
+        mac: 00:00:00:00:00:01
+        vlan: 100
+      requirements:
+        - port#port1/4:
+            node: port#port1/4
+            relationship: tosca.relationships.InterfaceOfPort
+
+    ips#10.0.4.2/24:
+      type: tosca.nodes.VRouterIp
+      properties:
+        ip: 10.0.4.2/24
+      requirements:
+        - interface#b1-1:
+            node: interface#b1-4
+            relationship: tosca.relationships.IpOfInterface
+
+    app#vrouterApp:
+      type: tosca.nodes.VRouterApp
+      properties:
+        name: org.onosproject.router
+        # can we use a relation to specify the connect point port?
+        control_plane_connect_point: of:00000000000000b1/5
+        ospf_enabled: true
+      requirements:
+          - service#vrouter:
+              node: service#vrouter
+              relationship: tosca.relationships.MemberOfService
+
diff --git a/roles/cord-profile/templates/vtn-service.yaml.j2 b/roles/cord-profile/templates/vtn-service.yaml.j2
new file mode 100644
index 0000000..7dc5d30
--- /dev/null
+++ b/roles/cord-profile/templates/vtn-service.yaml.j2
@@ -0,0 +1,52 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+imports:
+   - custom_types/xos.yaml
+
+description: Configures the VTN ONOS service
+
+topology_template:
+  node_templates:
+
+    service#ONOS_CORD:
+      type: tosca.nodes.ONOSService
+      requirements:
+      properties:
+          kind: onos
+          view_url: /admin/onos/onosservice/$id$/
+          no_container: true
+          rest_hostname: onos-cord
+          rest_port: 8182
+          replaces: service_ONOS_CORD
+
+    service#vtn:
+      type: tosca.nodes.VTNService
+      properties:
+          view_url: /admin/vtn/vtnservice/$id$/
+          privateGatewayMac: 00:00:00:00:00:01
+          localManagementIp: {{ management_network_ip }}
+          ovsdbPort: 6641
+          sshUser: root
+          sshKeyFile: /root/node_key
+          sshPort: 22
+          xosEndpoint: http://xos:{{ xos_ui_port }}/
+          xosUser: {{ xos_admin_user }}
+          xosPassword: {{ xos_admin_pass }}
+          replaces: service_vtn
+          vtnAPIVersion: 2
+          controllerPort: onos-cord:6654
+
+    VTN_ONOS_app:
+      type: tosca.nodes.ONOSVTNApp
+      requirements:
+          - onos_tenant:
+              node: service#ONOS_CORD
+              relationship: tosca.relationships.TenantOfService
+          - vtn_service:
+              node: service#vtn
+              relationship: tosca.relationships.UsedByService
+      properties:
+          install_dependencies: http://mavenrepo:8080/repository/org/opencord/cord-config/{{ cord_app_version }}/cord-config-{{ cord_app_version }}.oar,http://mavenrepo:8080/repository/org/opencord/vtn/{{ cord_app_version }}/vtn-{{ cord_app_version }}.oar
+          dependencies: org.onosproject.drivers, org.onosproject.drivers.ovsdb, org.onosproject.openflow-base, org.onosproject.ovsdb-base, org.onosproject.dhcp
+          autogenerate: vtn-network-cfg
+
diff --git a/roles/cord-profile/templates/xos-bootstrap-docker-compose.yaml.j2 b/roles/cord-profile/templates/xos-bootstrap-docker-compose.yaml.j2
new file mode 100644
index 0000000..bf79d65
--- /dev/null
+++ b/roles/cord-profile/templates/xos-bootstrap-docker-compose.yaml.j2
@@ -0,0 +1,98 @@
+version: '2'
+
+# XOS bootstrap docker compose
+# generated by platform-install/roles/cord-profile
+
+networks:
+{% for network in xos_docker_networks %}
+  {{ network }}:
+    external: true
+{% endfor %}
+
+services:
+  xos_db:
+    image: xosproject/xos-postgres
+    networks:
+{% for network in xos_docker_networks %}
+      - {{ network }}
+{% endfor %}
+    expose:
+      - "5432"
+
+{% if use_redis %}
+  xos_redis:
+    image: redis
+    networks:
+{% for network in xos_docker_networks %}
+     - {{ network }}
+{% endfor %}
+    logging:
+      driver: "json-file"
+      options:
+        max-size: "1000k"
+        max-file: "5"
+{% endif %}
+
+  xos_bootstrap_ui:
+    image: xosproject/xos
+    command: python /opt/xos/manage.py runserver 0.0.0.0:{{ xos_bootstrap_ui_port }} --insecure --makemigrations
+    networks:
+{% for network in xos_docker_networks %}
+     - {{ network }}
+{% endfor %}
+    labels:
+      org.xosproject.kind: userinterface
+      org.xosproject.target: bootstrap
+    links:
+      - xos_db
+{% if use_redis %}
+      - xos_redis:redis
+{% endif %}
+    volumes:
+      - .:/opt/cord_profile:ro
+      - ./xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
+{% for service in xos_services %}
+      - {{ cord_dir }}/{{ service.path }}:/opt/xos_services/{{ service.path | basename }}:ro
+{% endfor %}
+{% for library in xos_libraries %}
+      - {{ cord_dir }}/orchestration/xos_libraries/{{ library }}:/opt/xos_libraries/{{ library }}:ro
+{% endfor %}
+{% for volume in xos_docker_volumes %}
+      - {{ volume.host }}:{{ volume.container }}{{ ":rw" if (volume.read_only is defined and not volume.read_only ) else ":ro" }}
+{% endfor %}
+    ports:
+      - "{{ xos_bootstrap_ui_port }}:{{ xos_bootstrap_ui_port }}"
+    logging:
+      driver: "json-file"
+      options:
+        max-size: "1000k"
+        max-file: "5"
+
+  xos_synchronizer_onboarding:
+    image: xosproject/xos
+    command: bash -c "cd /opt/xos/synchronizers/onboarding; ./run.sh"
+    networks:
+{% for network in xos_docker_networks %}
+     - {{ network }}
+{% endfor %}
+    labels:
+      org.xosproject.kind: synchronizer
+      org.xosproject.target: onboarding
+    links:
+      - xos_db
+    volumes:
+      - /var/run/docker.sock:/var/run/docker.sock
+      - ./key_import:/opt/xos/key_import:ro
+      - ./onboarding-docker-compose:/opt/xos/synchronizers/onboarding/docker-compose
+{% for service in xos_services %}
+      - {{ cord_dir }}/{{ service.path }}:/opt/xos_services/{{ service.path | basename }}:ro
+{% endfor %}
+{% for library in xos_libraries %}
+      - {{ cord_dir }}/orchestration/xos_libraries/{{ library }}:/opt/xos_libraries/{{ library }}:ro
+{% endfor %}
+    logging:
+      driver: "json-file"
+      options:
+        max-size: "1000k"
+        max-file: "5"
+
diff --git a/roles/cord-profile/templates/xos.yaml.j2 b/roles/cord-profile/templates/xos.yaml.j2
new file mode 100644
index 0000000..626660c
--- /dev/null
+++ b/roles/cord-profile/templates/xos.yaml.j2
@@ -0,0 +1,87 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Onboard XOS
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+    xos:
+      type: tosca.nodes.XOS
+      properties:
+        ui_port: {{ xos_ui_port }}
+        bootstrap_ui_port: {{ xos_bootstrap_ui_port }}
+        docker_project_name: {{ cord_profile | regex_replace('\W','') }}
+        db_container_name: {{ cord_profile | regex_replace('\W','') }}bs_xos_db_1
+{% if use_redis %}
+        redis_container_name: {{ cord_profile | regex_replace('\W','') }}bs_xos_redis_1
+{% endif %}
+{% if frontend_only is defined %}
+        frontend_only: {{ frontend_only }}
+{% endif %}
+{% if source_ui_image is defined %}
+        source_ui_image: {{ source_ui_image }}
+{% endif %}
+
+    /opt/xos/xos_configuration/xos_common_config:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: {{ cord_profile_dir }}/xos_common_config
+          read_only: True
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
+    /opt/cord_profile:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: {{ cord_profile_dir }}
+          read_only: True
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
+{% for library in xos_libraries %}
+    /opt/xos_libraries/{{ library }}:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: {{ cord_dir }}/orchestration/xos_libraries/{{ library }}
+          read_only: True
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
+{% endfor %}
+
+
+{% for service in xos_services %}
+    /opt/xos_services/{{ service.path | basename }}:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: {{ cord_dir }}/{{ service.path }}
+          read_only: {{ service.read_only | default("True") }}
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
+{% endfor %}
+
+{% for volume in xos_docker_volumes %}
+    {{ volume.container }}:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: {{ volume.host }}
+          read_only: {{ volume.read_only | default("True") }}
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
+{% endfor %}
+
diff --git a/roles/cord-profile/templates/xos_common_config.j2 b/roles/cord-profile/templates/xos_common_config.j2
new file mode 100644
index 0000000..175be92
--- /dev/null
+++ b/roles/cord-profile/templates/xos_common_config.j2
@@ -0,0 +1,59 @@
+; xos_common_config
+; generated by platform-install/roles/cord-profile
+
+[plc]
+name=plc
+deployment=plc
+
+[db]
+name=xos
+user=postgres
+password=password
+host=xos_db
+port=5432
+
+[api]
+host=localhost
+port=8000
+ssl_key=None
+ssl_cert=None
+ca_ssl_cert=None
+ratelimit_enabled=0
+omf_enabled=0
+mail_support_address=support@localhost
+nova_enabled=True
+logfile=/var/log/xos.log
+
+[nova]
+admin_user=admin@domain.com
+admin_password=admin
+admin_tenant=admin
+url=http://localhost:5000/v2.0/
+default_image=None
+default_flavor=m1.small
+default_security_group=default
+ca_ssl_cert=/etc/ssl/certs/ca-certificates.crt
+
+[observer]
+pretend=False
+backoff_disabled=True
+images_directory=/opt/xos/images
+dependency_graph=/opt/xos/model-deps
+logfile=/var/log/xos_backend.log
+save_ansible_output=True
+node_key={{ cord_profile_dir }}/node_key
+
+[gui]
+disable_minidashboard={{ disable_minidashboard }}
+branding_name={{ gui_branding_name }}
+branding_icon={{ gui_branding_icon }}
+branding_favicon={{ gui_branding_favicon }}
+branding_bg={{ gui_branding_bg }}
+{% if gui_service_view_class %}
+service_view_class={{ gui_service_view_class }}
+{% endif %}
+
+{% if use_vtn %}
+[networking]
+use_vtn=True
+{% endif %}
diff --git a/roles/docker-install/defaults/main.yml b/roles/docker-install/defaults/main.yml
new file mode 100644
index 0000000..7ff3dde
--- /dev/null
+++ b/roles/docker-install/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+# docker-install/defaults/main.yml
+
+docker_apt_repo: "deb https://apt.dockerproject.org/repo ubuntu-trusty main"
+
diff --git a/roles/docker-install/files/docker_apt_key.gpg b/roles/docker-install/files/docker_apt_key.gpg
new file mode 100644
index 0000000..f63466b
--- /dev/null
+++ b/roles/docker-install/files/docker_apt_key.gpg
@@ -0,0 +1,47 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: SKS 1.1.5
+
+mQINBFWln24BEADrBl5p99uKh8+rpvqJ48u4eTtjeXAWbslJotmC/CakbNSqOb9oddfzRvGV
+eJVERt/Q/mlvEqgnyTQy+e6oEYN2Y2kqXceUhXagThnqCoxcEJ3+KM4RmYdoe/BJ/J/6rHOj
+q7Omk24z2qB3RU1uAv57iY5VGw5p45uZB4C4pNNsBJXoCvPnTGAs/7IrekFZDDgVraPx/hdi
+wopQ8NltSfZCyu/jPpWFK28TR8yfVlzYFwibj5WKdHM7ZTqlA1tHIG+agyPf3Rae0jPMsHR6
+q+arXVwMccyOi+ULU0z8mHUJ3iEMIrpTX+80KaN/ZjibfsBOCjcfiJSB/acn4nxQQgNZigna
+32velafhQivsNREFeJpzENiGHOoyC6qVeOgKrRiKxzymj0FIMLru/iFF5pSWcBQB7PYlt8J0
+G80lAcPr6VCiN+4cNKv03SdvA69dCOj79PuO9IIvQsJXsSq96HB+TeEmmL+xSdpGtGdCJHHM
+1fDeCqkZhT+RtBGQL2SEdWjxbF43oQopocT8cHvyX6Zaltn0svoGs+wX3Z/H6/8P5anog43U
+65c0A+64Jj00rNDr8j31izhtQMRo892kGeQAaaxg4Pz6HnS7hRC+cOMHUU4HA7iMzHrouAdY
+eTZeZEQOA7SxtCME9ZnGwe2grxPXh/U/80WJGkzLFNcTKdv+rwARAQABtDdEb2NrZXIgUmVs
+ZWFzZSBUb29sIChyZWxlYXNlZG9ja2VyKSA8ZG9ja2VyQGRvY2tlci5jb20+iQIcBBABCgAG
+BQJWw7vdAAoJEFyzYeVS+w0QHysP/i37m4SyoOCVcnybl18vzwBEcp4VCRbXvHvOXty1gccV
+IV8/aJqNKgBV97lY3vrpOyiIeB8ETQegsrxFE7t/Gz0rsLObqfLEHdmn5iBJRkhLfCpzjeOn
+yB3Z0IJB6UogO/msQVYe5CXJl6uwr0AmoiCBLrVlDAktxVh9RWch0l0KZRX2FpHu8h+uM0/z
+ySqIidlYfLa3y5oHscU+nGU1i6ImwDTD3ysZC5jp9aVfvUmcESyAb4vvdcAHR+bXhA/RW8QH
+eeMFliWw7Z2jYHyuHmDnWG2yUrnCqAJTrWV+OfKRIzzJFBs4e88ru5h2ZIXdRepw/+COYj34
+LyzxR2cxr2u/xvxwXCkSMe7F4KZAphD+1ws61FhnUMi/PERMYfTFuvPrCkq4gyBjt3fFpZ2N
+R/fKW87QOeVcn1ivXl9id3MMs9KXJsg7QasT7mCsee2VIFsxrkFQ2jNpD+JAERRn9Fj4ArHL
+5TbwkkFbZZvSi6fr5h2GbCAXIGhIXKnjjorPY/YDX6X8AaHOW1zblWy/CFr6VFl963jrjJga
+g0G6tNtBZLrclZgWhOQpeZZ5Lbvz2ZA5CqRrfAVcwPNW1fObFIRtqV6vuVluFOPCMAAnOnqR
+02w9t17iVQjO3oVN0mbQi9vjuExXh1YoScVetiO6LSmlQfVEVRTqHLMgXyR/EMo7iQIcBBAB
+CgAGBQJXSWBlAAoJEFyzYeVS+w0QeH0QAI6btAfYwYPuAjfRUy9qlnPhZ+xt1rnwsUzsbmo8
+K3XTNh+l/R08nu0dsczw30Q1wju28fh1N8ay223+69f0+yICaXqR18AbGgFGKX7vo0gfEVax
+dItUN3eHNydGFzmeOKbAlrxIMECnSTG/TkFVYO9Ntlv9vSN2BupmTagTRErxLZKnVsWRzp+X
+elwlgU5BCZ6U6Ze8+bIc6F1bZstf17X8i6XNV/rOCLx2yP0hn1osoljoLPpW8nzkwvqYsYbC
+A28lMt1aqe0UWvRCqR0zxlKn17NZQqjbxcajEMCajoQ01MshmO5GWePViv2abCZ/iaC5zKqV
+T3deMJHLq7lum6qhA41E9gJH9QoqT+qgadheeFfoC1QP7cke+tXmYg2R39p3l5Hmm+JQbP4f
+9V5mpWExvHGCSbcatr35tnakIJZugq2ogzsm1djCSz9222RXl9OoFqsm1bNzA78+/cOt5N2c
+yhU0bM2T/zgh42YbDD+JDU/HSmxUIpU+wrGvZGM2FU/up0DRxOC4U1fL6HHlj8liNJWfEg3v
+hougOh66gGF9ik5j4eIlNoz6lst+gmvlZQ9/9hRDeoG+AbhZeIlQ4CCw+Y1j/+fUxIzKHPVK
++aFJd+oJVNvbojJW/SgDdSMtFwqOvXyYcHl30Ws0gZUeDyAmNGZeJ3kFklnApDmeKK+OiQI4
+BBMBAgAiBQJVpZ9uAhsvBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRD3YiFXLFJgnbRf
+EAC9Uai7Rv20QIDlDogRzd+Vebg4ahyoUdj0CH+nAk40RIoq6G26u1e+sdgjpCa8jF6vrx+s
+mpgd1HeJdmpahUX0XN3X9f9qU9oj9A4I1WDalRWJh+tP5WNv2ySy6AwcP9QnjuBMRTnTK27p
+k1sEMg9oJHK5p+ts8hlSC4SluyMKH5NMVy9c+A9yqq9NF6M6d6/ehKfBFFLG9BX+XLBATvf1
+ZemGVHQusCQebTGv0C0V9yqtdPdRWVIEhHxyNHATaVYOafTj/EF0lDxLl6zDT6trRV5n9F1V
+CEh4Aal8L5MxVPcIZVO7NHT2EkQgn8CvWjV3oKl2GopZF8V4XdJRl90U/WDv/6cmfI08GkzD
+YBHhS8ULWRFwGKobsSTyIvnbk4NtKdnTGyTJCQ8+6i52s+C54PiNgfj2ieNn6oOR7d+bNCcG
+1CdOYY+ZXVOcsjl73UYvtJrO0Rl/NpYERkZ5d/tzw4jZ6FCXgggA/Zxcjk6Y1ZvIm8Mt8wLR
+FH9Nww+FVsCtaCXJLP8DlJLASMD9rl5QS9Ku3u7ZNrr5HWXPHXITX660jglyshch6CWeiUAT
+qjIAzkEQom/kEnOrvJAtkypRJ59vYQOedZ1sFVELMXg2UCkD/FwojfnVtjzYaTCeGwFQeqzH
+mM241iuOmBYPeyTY5veF49aBJA1gEJOQTvBR8Q==
+=74V2
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/roles/docker-install/handlers/main.yml b/roles/docker-install/handlers/main.yml
new file mode 100644
index 0000000..697fec7
--- /dev/null
+++ b/roles/docker-install/handlers/main.yml
@@ -0,0 +1,9 @@
+---
+# docker-install/handlers/main.yml
+
+- name: docker-restart
+  become: yes
+  service:
+    name: docker
+    state: restarted
+
diff --git a/roles/docker-install/tasks/main.yml b/roles/docker-install/tasks/main.yml
new file mode 100644
index 0000000..826d5bb
--- /dev/null
+++ b/roles/docker-install/tasks/main.yml
@@ -0,0 +1,48 @@
+---
+# docker-install/tasks/main.yml
+# note - all tasks run with become to preserve the `ansible_user_id` var
+
+- name: Prereqs and SSL support for apt for SSL
+  become: yes
+  apt:
+    name={{ item }}
+    update_cache=yes
+    cache_valid_time=3600
+  with_items:
+    - apt-transport-https
+    - ca-certificates
+    - python-pip
+
+- name: Trust docker apt key
+  become: yes
+  apt_key:
+    data: "{{ lookup('file', 'docker_apt_key.gpg') }}"
+
+- name: Add docker apt repo
+  become: yes
+  apt_repository:
+    repo: "{{ docker_apt_repo }}"
+
+- name: Install docker
+  become: yes
+  apt:
+    update_cache=yes
+    cache_valid_time=3600
+    name=docker-engine
+
+# docker fails without docker-py, docker-compose >1.9 fails with docker-py installed
+- name: Install docker-compose and docker-py
+  become: yes
+  pip:
+    name: "{{ item }}"
+  with_items:
+    - docker-py
+    - docker-compose==1.9
+
+- name: Make current user part of the Docker group
+  become: yes
+  user:
+    name: "{{ ansible_user_id }}"
+    groups: "docker"
+    append: yes
+
diff --git a/roles/exampleservice-config/defaults/main.yml b/roles/exampleservice-config/defaults/main.yml
new file mode 100644
index 0000000..82098e0
--- /dev/null
+++ b/roles/exampleservice-config/defaults/main.yml
@@ -0,0 +1,6 @@
+---
+# exampleservice-config/defaults/main.yml
+
+cord_dir: "{{ ansible_user_dir + '/cord' }}"
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
diff --git a/roles/exampleservice-config/tasks/main.yml b/roles/exampleservice-config/tasks/main.yml
new file mode 100644
index 0000000..dc21c17
--- /dev/null
+++ b/roles/exampleservice-config/tasks/main.yml
@@ -0,0 +1,29 @@
+---
+# exampleservice-config/tasks/main.yml
+
+- name: Create fake/empty ssh keys if profile hasn't created them
+  copy:
+    remote_src: True # file is local to the remote machine
+    force: False # only copy if destination file doesn't exist
+    src: "/dev/null"
+    dest: "{{ cord_profile_dir }}/key_import/{{ item }}"
+    mode: 0600
+  with_items:
+    - exampleservice_rsa
+    - exampleservice_rsa.pub
+
+- name: Copy exampleservice onboarding TOSCA files to cord_profile
+  copy:
+    src: "{{ cord_dir }}/orchestration/xos_services/exampleservice/xos/exampleservice-onboard.yaml"
+    dest: "{{ cord_profile_dir }}/exampleservice-onboard.yaml"
+
+- name: TOSCA to mount exampleservice volume in XOS container
+  template:
+    src: "xos-exampleservice.yaml.j2"
+    dest: "{{ cord_profile_dir }}/xos-exampleservice.yaml"
+
+- name: TOSCA to create exampleservice test config
+  template:
+    src: "test-exampleservice.yaml.j2"
+    dest: "{{ cord_profile_dir }}/test-exampleservice.yaml"
+
diff --git a/roles/xos-install/templates/exampleservice.yaml.j2 b/roles/exampleservice-config/templates/test-exampleservice.yaml.j2
similarity index 100%
rename from roles/xos-install/templates/exampleservice.yaml.j2
rename to roles/exampleservice-config/templates/test-exampleservice.yaml.j2
diff --git a/roles/exampleservice-config/templates/xos-exampleservice.yaml.j2 b/roles/exampleservice-config/templates/xos-exampleservice.yaml.j2
new file mode 100644
index 0000000..441e075
--- /dev/null
+++ b/roles/exampleservice-config/templates/xos-exampleservice.yaml.j2
@@ -0,0 +1,24 @@
+---
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: Have the XOS container mount the exampleservice volume
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+
+    xos:
+      type: tosca.nodes.XOS
+
+    /opt/xos_services/exampleservice:
+      type: tosca.nodes.XOSVolume
+      properties:
+          host_path: "{{ cord_dir }}/orchestration/xos_services/exampleservice"
+          read_only: True
+      requirements:
+          - xos:
+             node: xos
+             relationship: tosca.relationships.UsedByXOS
+
diff --git a/roles/exampleservice-onboard/defaults/main.yml b/roles/exampleservice-onboard/defaults/main.yml
new file mode 100644
index 0000000..463326e
--- /dev/null
+++ b/roles/exampleservice-onboard/defaults/main.yml
@@ -0,0 +1,8 @@
+---
+# exampleservice-onboard/defaults/main.yml
+
+cord_dir: "{{ ansible_user_dir + '/cord' }}"
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
+xos_bootstrap_ui_port: 9001
+
diff --git a/roles/exampleservice-onboard/tasks/main.yml b/roles/exampleservice-onboard/tasks/main.yml
new file mode 100644
index 0000000..ba559d1
--- /dev/null
+++ b/roles/exampleservice-onboard/tasks/main.yml
@@ -0,0 +1,43 @@
+---
+# exampleservice-onboard/tasks/main.yml
+
+- name: Disable onboarding
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/disable-onboarding.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Have XOS container mount exampleservice volume
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/xos-exampleservice.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Onboard exampleservice
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/exampleservice-onboard.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Enable onboarding
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/enable-onboarding.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Wait for exampleservice to be onboarded
+  uri:
+    url: "http://localhost:{{ xos_bootstrap_ui_port }}/api/utility/onboarding/services/exampleservice/ready/"
+    method: GET
+    return_content: yes
+  register: xos_onboard_status
+  until: '"true" in xos_onboard_status.content'
+  retries: 60
+  delay: 2
+
+- name: Wait for XOS to be onboarded after exampleservice onboarding
+  uri:
+    url: "http://localhost:{{ xos_bootstrap_ui_port }}/api/utility/onboarding/xos/ready/"
+    method: GET
+    return_content: yes
+  register: xos_onboard_status
+  until: '"true" in xos_onboard_status.content'
+  retries: 60
+  delay: 2
+
diff --git a/roles/juju-compute-setup/tasks/main.yml b/roles/juju-compute-setup/tasks/main.yml
index 7473a06..d061697 100644
--- a/roles/juju-compute-setup/tasks/main.yml
+++ b/roles/juju-compute-setup/tasks/main.yml
@@ -68,8 +68,8 @@
   action: shell bash -c "source ~/admin-openrc.sh; nova hypervisor-list | grep '{{ item }}'"
   register: result
   until: result | success
-  retries: 5
-  delay: 5
+  retries: 10
+  delay: 10
   with_items: "{{ groups['compute'] }}"
   tags:
    - skip_ansible_lint # this really should be the os_server module, but ansible doesn't know about juju created openstack
diff --git a/roles/juju-setup/templates/opencloud_juju_config.yml.j2 b/roles/juju-setup/templates/opencloud_juju_config.yml.j2
index 564f28f..4379a9f 100644
--- a/roles/juju-setup/templates/opencloud_juju_config.yml.j2
+++ b/roles/juju-setup/templates/opencloud_juju_config.yml.j2
@@ -19,8 +19,6 @@
 
 mongodb: {}
 
-nagios: {}
-
 neutron-api:
   flat-network-providers: "*"
   openstack-origin: "cloud:trusty-kilo"
@@ -49,8 +47,6 @@
   config-flags: "firewall_driver=nova.virt.firewall.NoopFirewallDriver"
   openstack-origin: "cloud:trusty-kilo"
 
-nrpe: {}
-
 ntp:
   source: "0.ubuntu.pool.ntp.org 1.ubuntu.pool.ntp.org 2.ubuntu.pool.ntp.org 3.ubuntu.pool.ntp.org"
 
diff --git a/roles/maas-test-client-install/tasks/main.yml b/roles/maas-test-client-install/tasks/main.yml
index 5e4eb06..6b225b9 100644
--- a/roles/maas-test-client-install/tasks/main.yml
+++ b/roles/maas-test-client-install/tasks/main.yml
@@ -1,4 +1,6 @@
 ---
+# maas-test-client-install/tasks/main.yml
+
 - name: Create testclient LXD profile
   lxd_profile:
     name: testclient
@@ -49,3 +51,4 @@
   until: result | success
   retries: 3
   delay: 10
+
diff --git a/roles/platform-check/defaults/main.yml b/roles/platform-check/defaults/main.yml
index 666162d..16a8ef7 100644
--- a/roles/platform-check/defaults/main.yml
+++ b/roles/platform-check/defaults/main.yml
@@ -2,3 +2,7 @@
 # platform-check/defaults/main.yml
 
 onos_cord_dest: "{{ ansible_user_dir }}/onos-cord/"
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
+xos_ui_port: 9000
+
diff --git a/roles/platform-check/tasks/main.yml b/roles/platform-check/tasks/main.yml
index 035945f..a67e837 100644
--- a/roles/platform-check/tasks/main.yml
+++ b/roles/platform-check/tasks/main.yml
@@ -20,11 +20,31 @@
   tags:
     - skip_ansible_lint
 
-- name: Tell XOS to refresh VTN configuration
+- name: Tell XOS to refresh VTN Service and compute nodes
   when: result | failed
-  make:
-    chdir: "{{ service_profile_repo_dest }}/{{ xos_configuration }}"
-    target: vtn
+  xostosca:
+    url: "http://xos.{{ site_suffix }}:{{ xos_ui_port }}/api/utility/tosca/run/"
+    user: "{{ xos_admin_user }}"
+    password:  "{{ xos_admin_pass }}"
+    recipe: "{{ lookup('file', cord_profile_dir + '/' + item ) }}"
+  with_items:
+    - openstack.yaml
+    - openstack-compute.yaml
+    - vtn-service.yaml
+
+- name: Pause to work around race in VTN or ONOS synchronizers
+  pause:
+    seconds: 20
+
+- name: Enable VTN for OpenStack Compute nodes
+  when: result | failed
+  xostosca:
+    url: "http://xos.{{ site_suffix }}:{{ xos_ui_port }}/api/utility/tosca/run/"
+    user: "{{ xos_admin_user }}"
+    password:  "{{ xos_admin_pass }}"
+    recipe: "{{ lookup('file', cord_profile_dir + '/' + item ) }}"
+  with_items:
+    - openstack-compute-vtn.yaml
 
 - name: Ensure br-int exists on all compute nodes (check VTN #2)
   when: result | failed
diff --git a/roles/repo/defaults/main.yml b/roles/repo/defaults/main.yml
new file mode 100644
index 0000000..b2af59f
--- /dev/null
+++ b/roles/repo/defaults/main.yml
@@ -0,0 +1,21 @@
+---
+# repo/defaults/main.yml
+
+cord_dir: "{{ ansible_user_dir + '/cord' }}"
+repo_dl_url: "https://storage.googleapis.com/git-repo-downloads/repo"
+
+# This is for repo v1.23, and will change, as repo_dl_url unfortunately lacks a version...
+repo_checksum: "sha256:e147f0392686c40cfd7d5e6f332c6ee74c4eab4d24e2694b3b0a0c037bf51dc5"
+
+repo_manifest_url: "https://gerrit.opencord.org/manifest"
+
+# Used to download specific gerrit changesets. Syntax is:
+#
+# gerrit_changsets:
+#  - path: build/platform-install
+#    revision: 2934/19
+#  - path: ....
+#    revision: #/#
+#
+gerrit_changesets: []
+
diff --git a/roles/repo/tasks/main.yml b/roles/repo/tasks/main.yml
new file mode 100644
index 0000000..779e406
--- /dev/null
+++ b/roles/repo/tasks/main.yml
@@ -0,0 +1,38 @@
+---
+# repo/tasks/main.yml
+
+- name: Download and install repo tool
+  become: yes
+  get_url:
+    url: "{{ repo_dl_url }}"
+    checksum: "{{ repo_checksum }}"
+    dest: "/usr/local/bin/repo"
+    owner: root
+    group: root
+    mode: 0755
+
+- name: Create CORD directory
+  file:
+    dest: "{{ cord_dir }}"
+    state: directory
+
+- name: Init CORD repos (master branch) using repo
+  command: "/usr/local/bin/repo init -u {{ repo_manifest_url }} -b master -g build,onos,orchestration"
+  args:
+    chdir: "{{ cord_dir }}"
+    creates: "{{ cord_dir }}/.repo"
+
+- name: Synchronize CORD repos using repo
+  command: "repo sync"
+  args:
+    chdir: "{{ cord_dir }}"
+    creates: "{{ cord_dir }}/build"
+
+- name: Download specific gerrit changesets using repo
+  command: "/usr/local/bin/repo download {{ item.path }} {{ item.revision }}"
+  args:
+    chdir: "{{ cord_dir }}"
+  with_items: "{{ gerrit_changesets }}"
+  tags:
+    - skip_ansible_lint # usually won't be run, except during dev
+
diff --git a/roles/teardown-profile/defaults/main.yml b/roles/teardown-profile/defaults/main.yml
new file mode 100644
index 0000000..42ace11
--- /dev/null
+++ b/roles/teardown-profile/defaults/main.yml
@@ -0,0 +1,10 @@
+---
+# teardown-profile/defaults/main.yml
+
+cord_dir: "{{ ansible_user_dir + '/cord' }}"
+
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
+xos_docker_networks:
+  - "xos"
+
diff --git a/roles/teardown-profile/tasks/main.yml b/roles/teardown-profile/tasks/main.yml
new file mode 100644
index 0000000..3c9bbea
--- /dev/null
+++ b/roles/teardown-profile/tasks/main.yml
@@ -0,0 +1,37 @@
+---
+# teardown-profile/tasks/main.yml
+# Destroys the currently created profile
+# NOTE: ignoring errors so that incomplete builds can be removed
+
+- name: Stop and remove XOS containers
+  docker_service:
+    project_name: "{{ cord_profile | regex_replace('\\W','') }}"
+    project_src: "{{ cord_profile_dir }}/onboarding-docker-compose/"
+    state: absent
+    remove_images: local
+  ignore_errors: yes
+
+- name: Stop and remove XOS bootstrap containers
+  docker_service:
+    project_name: "{{ cord_profile | regex_replace('\\W','') }}bs"
+    project_src: "{{ cord_profile_dir }}"
+    files: "xos-bootstrap-docker-compose.yaml"
+    state: absent
+    remove_images: local
+  ignore_errors: yes
+
+# need to remove images using docker_images here?
+
+- name: Remove docker networks
+  docker_network:
+    name: "{{ item }}"
+    state: absent
+  with_items: "{{ xos_docker_networks }}"
+  ignore_errors: yes
+
+- name: Remove the cord_profile directory
+  file:
+    path: "{{ cord_profile_dir }}"
+    state: absent
+  ignore_errors: yes
+
diff --git a/roles/test-exampleservice/defaults/main.yml b/roles/test-exampleservice/defaults/main.yml
new file mode 100644
index 0000000..04b252e
--- /dev/null
+++ b/roles/test-exampleservice/defaults/main.yml
@@ -0,0 +1,6 @@
+---
+# test-exampleservice/defaults/main.yml
+
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+xos_ui_port: 9000
+
diff --git a/roles/test-exampleservice/tasks/main.yml b/roles/test-exampleservice/tasks/main.yml
index 331dfcf..12bf2af 100644
--- a/roles/test-exampleservice/tasks/main.yml
+++ b/roles/test-exampleservice/tasks/main.yml
@@ -1,12 +1,13 @@
 ---
 # test-examplservice/tasks/main.yml
-#
 # Run tests to check that the single-node deployment has worked
 
-- name: Onboard ExampleService and instantiate a VM
-  make:
-    chdir: "{{ service_profile_repo_dest }}/{{ xos_configuration }}"
-    target: exampleservice
+- name: Load TOSCA to apply test config for ExampleService, over REST
+  xostosca:
+    url: "http://xos.{{ site_suffix }}:{{ xos_ui_port }}/api/utility/tosca/run/"
+    user: "{{ xos_admin_user }}"
+    password:  "{{ xos_admin_pass }}"
+    recipe: "{{ lookup('file', cord_profile_dir + '/test-exampleservice.yaml' ) }}"
 
 - name: Wait for ExampleService VM to come up
   shell: bash -c "source ~/admin-openrc.sh; nova list --all-tenants|grep 'exampleservice.*ACTIVE' > /dev/null"
@@ -82,3 +83,4 @@
 
 - name: Output from curl test
   debug: var=curltest.stdout_lines
+
diff --git a/roles/test-subscriber-config/defaults/main.yml b/roles/test-subscriber-config/defaults/main.yml
new file mode 100644
index 0000000..3fbf456
--- /dev/null
+++ b/roles/test-subscriber-config/defaults/main.yml
@@ -0,0 +1,5 @@
+---
+# test-subscriber-config/defaults/main.yml
+
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
diff --git a/roles/test-subscriber-config/tasks/main.yml b/roles/test-subscriber-config/tasks/main.yml
new file mode 100644
index 0000000..46c1642
--- /dev/null
+++ b/roles/test-subscriber-config/tasks/main.yml
@@ -0,0 +1,10 @@
+---
+# test-subscriber/tasks/main.yml
+
+- name: Create test-subscriber.yaml TOSCA config
+  template:
+    src: test-subscriber.yaml.j2
+    dest: "{{ cord_profile_dir }}/test-subscriber.yaml"
+    owner: "{{ ansible_user_id }}"
+    mode: 0644
+
diff --git a/roles/xos-install/templates/cord-test-subscriber.yaml.j2 b/roles/test-subscriber-config/templates/test-subscriber.yaml.j2
similarity index 100%
rename from roles/xos-install/templates/cord-test-subscriber.yaml.j2
rename to roles/test-subscriber-config/templates/test-subscriber.yaml.j2
diff --git a/roles/test-subscriber-enable/tasks/main.yml b/roles/test-subscriber-enable/tasks/main.yml
new file mode 100644
index 0000000..eb7b66e
--- /dev/null
+++ b/roles/test-subscriber-enable/tasks/main.yml
@@ -0,0 +1,8 @@
+---
+# test-subscriber-enable/tasks/main.yml
+
+- name: Run TOSCA to add test-subscriber
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/test-subscriber.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
diff --git a/roles/test-vsg/tasks/main.yml b/roles/test-vsg/tasks/main.yml
index 4a44e7b..5e029f7 100644
--- a/roles/test-vsg/tasks/main.yml
+++ b/roles/test-vsg/tasks/main.yml
@@ -3,11 +3,6 @@
 #
 # Run tests to check that the CORD-in-a-Box deployment has worked.
 
-- name: Create cord subscriber
-  make:
-    chdir: "{{ service_profile_repo_dest }}/{{ xos_configuration }}"
-    target: cord-subscriber
-
 - name: Get name of compute node
   shell: bash -c "source ~/admin-openrc.sh; nova service-list|grep nova-compute|cut -d '|' -f 3"
   register: node_name
@@ -69,3 +64,4 @@
 
 - name: Output from ping test
   debug: var=pingtest.stdout_lines
+
diff --git a/roles/tosca-tests/tasks/main.yml b/roles/tosca-tests/tasks/main.yml
new file mode 100644
index 0000000..f244f5d
--- /dev/null
+++ b/roles/tosca-tests/tasks/main.yml
@@ -0,0 +1,19 @@
+---
+# tosca-tests/tasks/main.yml
+
+- name: Run TOSCA tests
+  command: "python ./alltests.py"
+  args:
+    chdir: "/opt/xos/tosca/tests"
+  register: tosca_tests_out
+  ignore_errors: yes
+  tags:
+    - skip_ansible_lint # run during testing only
+
+- name: Save output from TOSCA tests
+  copy:
+    content: "{{ tosca_tests_out.stdout_lines }}"
+    dest: "/tmp/tosca-tests.out"
+
+- name: Print output from TOSCA test
+  debug: var=tosca_tests_out.stdout_lines
diff --git a/roles/xos-bootstrap-hosts/defaults/main.yml b/roles/xos-bootstrap-hosts/defaults/main.yml
new file mode 100644
index 0000000..6b20046
--- /dev/null
+++ b/roles/xos-bootstrap-hosts/defaults/main.yml
@@ -0,0 +1,7 @@
+---
+# xos-bootstrap-hosts/defaults/main.yml
+
+cord_dir: "{{ ansible_user_dir + '/cord' }}"
+
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
diff --git a/roles/xos-bootstrap-hosts/tasks/main.yml b/roles/xos-bootstrap-hosts/tasks/main.yml
new file mode 100644
index 0000000..32a589c
--- /dev/null
+++ b/roles/xos-bootstrap-hosts/tasks/main.yml
@@ -0,0 +1,20 @@
+---
+# xos-bootstrap-hosts/tasks/main.yml
+
+- name: Get the Docker container names for bootstrap containers
+  docker_service:
+    project_name: "{{ cord_profile | regex_replace('\\W','') }}bs"
+    project_src: "{{ cord_profile_dir }}"
+    files: "xos-bootstrap-docker-compose.yaml"
+    recreate: never
+  register: xos_bootstrap_out
+
+- name: Add the containers to Ansible groups on a per-container type basis
+  add_host:
+    name: "{{ xos_bootstrap_out.ansible_facts[item].keys() | first }}"
+    groups: "{{ item }}"
+    ansible_connection: "docker"
+    cord_profile: "{{ cord_profile }}"
+    ansible_ssh_user: "root"
+  with_items: "{{ xos_bootstrap_out.ansible_facts.keys() | list }}"
+
diff --git a/roles/xos-bootstrap/defaults/main.yml b/roles/xos-bootstrap/defaults/main.yml
new file mode 100644
index 0000000..ada7671
--- /dev/null
+++ b/roles/xos-bootstrap/defaults/main.yml
@@ -0,0 +1,8 @@
+---
+# xos-bootstrap/defaults/main.yml
+
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
+xos_docker_networks:
+  - "xos"
+
diff --git a/roles/xos-bootstrap/tasks/main.yml b/roles/xos-bootstrap/tasks/main.yml
new file mode 100644
index 0000000..109d9ee
--- /dev/null
+++ b/roles/xos-bootstrap/tasks/main.yml
@@ -0,0 +1,15 @@
+---
+# xos-bootstrap/tasks/main.yml
+
+- name: Create docker networks
+  docker_network:
+    name: "{{ item }}"
+  with_items: "{{ xos_docker_networks }}"
+
+- name: Start XOS bootstrap containers
+  docker_service:
+    project_name: "{{ cord_profile | regex_replace('\\W','') }}bs"
+    project_src: "{{ cord_profile_dir }}"
+    files: "xos-bootstrap-docker-compose.yaml"
+  register: xos_bootstrap_out
+
diff --git a/roles/xos-clear-db/files/xos_clear_db.sql b/roles/xos-clear-db/files/xos_clear_db.sql
new file mode 100644
index 0000000..879cbc7
--- /dev/null
+++ b/roles/xos-clear-db/files/xos_clear_db.sql
@@ -0,0 +1,24 @@
+-- Clear the XOS database (used for testing)
+
+CREATE OR REPLACE FUNCTION truncate_tables(username IN VARCHAR) RETURNS void AS $$
+DECLARE
+  statements CURSOR FOR
+    SELECT tablename FROM pg_tables
+    WHERE tableowner = username AND schemaname = 'public';
+BEGIN
+  FOR stmt IN statements LOOP
+    EXECUTE 'TRUNCATE TABLE ' || quote_ident(stmt.tablename) || ' CASCADE;';
+  END LOOP;
+END;
+$$ LANGUAGE plpgsql;
+
+SELECT truncate_tables('postgres');
+
+SELECT setval('core_tenant_id_seq', 1);
+
+SELECT setval('core_deployment_id_seq', 1);
+
+SELECT setval('core_flavor_id_seq', 1);
+
+SELECT setval('core_service_id_seq', 1);
+
diff --git a/roles/xos-clear-db/tasks/main.yml b/roles/xos-clear-db/tasks/main.yml
new file mode 100644
index 0000000..3fb46a4
--- /dev/null
+++ b/roles/xos-clear-db/tasks/main.yml
@@ -0,0 +1,13 @@
+---
+# xos-clear-db/tasks/main.yml
+
+- name: Copy over database cleanup script
+  copy:
+    src: xos_clear_db.sql
+    dest: /tmp/xos_clear_db.sql
+
+- name: Run database cleanup script
+  command: "psql -U postgres -d xos -f /tmp/xos_clear_db.sql"
+  tags:
+    - skip_ansible_lint # test scenario destructive script
+
diff --git a/roles/xos-compute-setup/tasks/main.yml b/roles/xos-compute-setup/tasks/main.yml
deleted file mode 100644
index d8de0ed..0000000
--- a/roles/xos-compute-setup/tasks/main.yml
+++ /dev/null
@@ -1,20 +0,0 @@
----
-# xos-compute-setup/tasks/main.yml
-#
-# Tell XOS that a new compute node has been added
-
-- name: Create nodes/vtn TOSCA config
-  template:
-    src: "{{ item }}.j2"
-    dest: "{{ service_profile_repo_dest }}/{{ xos_configuration }}/{{ item }}"
-    owner: "{{ ansible_user_id }}"
-    mode: 0644
-  with_items:
-    - vtn.yaml
-    - nodes.yaml
-
-- name: Rebuild VTN configuration with new nodes block
-  make:
-    chdir: "{{ service_profile_repo_dest }}/{{ xos_configuration }}"
-    target: vtn
-
diff --git a/roles/xos-compute-setup/templates/vtn.yaml.j2 b/roles/xos-compute-setup/templates/vtn.yaml.j2
deleted file mode 100644
index f162609..0000000
--- a/roles/xos-compute-setup/templates/vtn.yaml.j2
+++ /dev/null
@@ -1,103 +0,0 @@
-tosca_definitions_version: tosca_simple_yaml_1_0
-
-imports:
-   - custom_types/xos.yaml
-
-description: autogenerated node tags file for VTN configuration
-
-topology_template:
-  node_templates:
-
-    service#ONOS_CORD:
-      type: tosca.nodes.ONOSService
-      requirements:
-      properties:
-          kind: onos
-          view_url: /admin/onos/onosservice/$id$/
-          no_container: true
-          rest_hostname: onos-cord
-          rest_port: 8182
-          replaces: service_ONOS_CORD
-
-    service#vtn:
-      type: tosca.nodes.VTNService
-      properties:
-          view_url: /admin/vtn/vtnservice/$id$/
-          privateGatewayMac: 00:00:00:00:00:01
-          localManagementIp: {{ management_network_ip }}
-          ovsdbPort: 6641
-          sshUser: root
-          sshKeyFile: /root/node_key
-          sshPort: 22
-          xosEndpoint: http://xos:8888/
-          xosUser: padmin@vicci.org
-          xosPassword: letmein
-          replaces: service_vtn
-          vtnAPIVersion: 2
-          controllerPort: onos-cord:6654
-
-{% for node in groups["compute"] %}
-{% if 'ipv4' in hostvars[node]['ansible_fabric'] %}
-
-    {{ hostvars[node]['ansible_hostname'] }}:
-      type: tosca.nodes.Node
-
-    # VTN bridgeId field for node {{ hostvars[node]['ansible_hostname'] }}
-    {{ hostvars[node]['ansible_hostname'] }}_bridgeId_tag:
-      type: tosca.nodes.Tag
-      properties:
-          name: bridgeId
-          value: of:0000{{ hostvars[node]['ansible_fabric']['macaddress'] | hwaddr('bare') }}
-      requirements:
-          - target:
-              node: {{ hostvars[node]['ansible_hostname'] }}
-              relationship: tosca.relationships.TagsObject
-          - service:
-              node: service#ONOS_CORD
-              relationship: tosca.relationships.MemberOfService
-
-    # VTN dataPlaneIntf field for node {{ hostvars[node]['ansible_hostname'] }}
-    {{ hostvars[node]['ansible_hostname'] }}_dataPlaneIntf_tag:
-      type: tosca.nodes.Tag
-      properties:
-          name: dataPlaneIntf
-          value: fabric
-      requirements:
-          - target:
-              node: {{ hostvars[node]['ansible_hostname'] }}
-              relationship: tosca.relationships.TagsObject
-          - service:
-              node: service#ONOS_CORD
-              relationship: tosca.relationships.MemberOfService
-
-    # VTN dataPlaneIp field for node {{ hostvars[node]['ansible_hostname'] }}
-    {{ hostvars[node]['ansible_hostname'] }}_dataPlaneIp_tag:
-      type: tosca.nodes.Tag
-      properties:
-          name: dataPlaneIp
-          value: {{ ( hostvars[node]['ansible_fabric']['ipv4']['address'] ~ '/' ~ hostvars[node]['ansible_fabric']['ipv4']['netmask'] ) | ipaddr('cidr') }}
-      requirements:
-          - target:
-              node: {{ hostvars[node]['ansible_hostname'] }}
-              relationship: tosca.relationships.TagsObject
-          - service:
-              node: service#ONOS_CORD
-              relationship: tosca.relationships.MemberOfService
-
-{% endif %}
-{% endfor %}
-
-    VTN_ONOS_app:
-      type: tosca.nodes.ONOSVTNApp
-      requirements:
-          - onos_tenant:
-              node: service#ONOS_CORD
-              relationship: tosca.relationships.TenantOfService
-          - vtn_service:
-              node: service#vtn
-              relationship: tosca.relationships.UsedByService
-      properties:
-          install_dependencies: http://mavenrepo:8080/repository/org/opencord/cord-config/{{ cord_app_version}}/cord-config-{{ cord_app_version }}.oar,http://mavenrepo:8080/repository/org/opencord/vtn/{{ cord_app_version }}/vtn-{{ cord_app_version }}.oar
-          dependencies: org.onosproject.drivers, org.onosproject.drivers.ovsdb, org.onosproject.openflow-base, org.onosproject.ovsdb-base, org.onosproject.dhcp
-          autogenerate: vtn-network-cfg
-
diff --git a/roles/xos-config/defaults/main.yml b/roles/xos-config/defaults/main.yml
new file mode 100644
index 0000000..c610f28
--- /dev/null
+++ b/roles/xos-config/defaults/main.yml
@@ -0,0 +1,6 @@
+---
+# xos-config/defaults/main.yml
+
+xos_admin_user: "xosadmin@opencord.org"
+
+xos_tosca_config_templates: []
diff --git a/roles/xos-config/tasks/main.yml b/roles/xos-config/tasks/main.yml
index 9898d3e..2e5f9cc 100644
--- a/roles/xos-config/tasks/main.yml
+++ b/roles/xos-config/tasks/main.yml
@@ -1,15 +1,9 @@
 ---
-# xos-head-start/tasks/main.yml
+# xos-config/tasks/main.yml
 
-# Performs any configuration of XOS that should be done right before starting
-# XOS. This includes copying the admin-openrc.sh, since we had to wait for juju
-# to finish before admin-openrc.sh was present.
-
-- name: Copy admin-openrc.sh to service-profile
-#  command: cp ~/admin-openrc.sh {{ service_profile_repo_dest }}/{{ xos_configuration }}
-  copy:
-      remote_src=True
-      src=~/admin-openrc.sh
-      dest={{ service_profile_repo_dest }}/{{ xos_configuration }}
-
+- name: Configure XOS with profile specific TOSCA
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/{{ item }}"
+  with_items: "{{ xos_tosca_config_templates }}"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
 
diff --git a/roles/xos-docker-images/defaults/main.yml b/roles/xos-docker-images/defaults/main.yml
new file mode 100644
index 0000000..22943b2
--- /dev/null
+++ b/roles/xos-docker-images/defaults/main.yml
@@ -0,0 +1,14 @@
+---
+# xos-docker-images/defaults/main.yml
+
+cord_dir: "{{ ansible_user_dir + '/cord' }}"
+
+build_xos_base_image: False
+build_xos_test_image: False
+
+deploy_docker_registry: "localhost:5000"
+deploy_docker_tag: "latest"
+
+push_xos_base_image: False
+push_xos_image: False
+
diff --git a/roles/xos-docker-images/tasks/main.yml b/roles/xos-docker-images/tasks/main.yml
new file mode 100644
index 0000000..4482aac
--- /dev/null
+++ b/roles/xos-docker-images/tasks/main.yml
@@ -0,0 +1,59 @@
+---
+# xos-docker-images/tasks/main.yml
+
+- name: Build xos-base docker image
+  when: build_xos_base_image
+  docker_image:
+    name: "xosproject/xos-base"
+    path: "{{ cord_dir }}/orchestration/xos/containers/xos"
+    dockerfile: "Dockerfile.base"
+
+- name: Pull xos-base docker image from Dockerhub
+  when: not build_xos_base_image
+  docker_image:
+    name: "xosproject/xos-base"
+
+- name: Obtain XOS git repo metadata
+  command: "git log --pretty=format:'{\"XOS_GIT_COMMIT_DATE\":\"%ci\", \"XOS_GIT_COMMIT_HASH\":\"%H\"}' -n 1"
+  args:
+    chdir: "{{ cord_dir }}/orchestration/xos/"
+  register: xos_git_metadata
+  tags:
+    - skip_ansible_lint # idempotent git metadata retrieval, git module can't do this
+
+- name: Copy over SSL CA certificates
+  copy:
+    src: "{{ playbook_dir }}/pki/intermediate_ca/certs/im_cert_chain.pem"
+    dest: "{{ cord_dir }}/orchestration/xos/containers/xos/local_certs.crt"
+    mode: 0644
+
+- name: Build xosproject/xos devel image
+  docker_image:
+    name: "xosproject/xos"
+    path: "{{ cord_dir }}/orchestration/xos/"
+    dockerfile: "containers/xos/Dockerfile.devel"
+    buildargs: "{{ xos_git_metadata.stdout }}"
+    pull: False # should use locally created, or already pulled xos-base image
+
+- name: Build xosproject/xos-test testing image
+  when: build_xos_test_image
+  docker_image:
+    name: "xosproject/xos-test"
+    path: "{{ cord_dir }}/orchestration/xos/"
+    dockerfile: "containers/xos/Dockerfile.test"
+    pull: False # use the locally built copy of xosproject/xos
+
+- name: Tag and push xos-base image to docker registry
+  when: push_xos_base_image
+  docker_image:
+    name: "{{ deploy_docker_registry }}/xosproject/xos-base"
+    tag: "{{ deploy_docker_tag }}"
+    push: yes
+
+- name: Tag and push xos image to docker registry
+  when: push_xos_image
+  docker_image:
+    name: "{{ deploy_docker_registry }}/xosproject/xos"
+    tag: "{{ deploy_docker_tag }}"
+    push: True
+
diff --git a/roles/xos-install/defaults/main.yml b/roles/xos-install/defaults/main.yml
deleted file mode 100644
index fe04cec..0000000
--- a/roles/xos-install/defaults/main.yml
+++ /dev/null
@@ -1,18 +0,0 @@
----
-# default variables for xos-install role
-
-xos_repo_url: "https://gerrit.opencord.org/xos"
-xos_repo_dest: "{{ ansible_user_dir }}/xos"
-xos_repo_branch: "HEAD"
-
-xos_configuration: "devel"
-
-service_profile_repo_url: "https://gerrit.opencord.org/p/service-profile.git"
-service_profile_repo_dest: "{{ ansible_user_dir }}/service-profile"
-service_profile_repo_branch: "HEAD"
-
-docker_tag: "latest"
-docker_registry: "docker.io"
-local_docker_registry: "docker-registry:5000"
-
-cord_dest_dir: "/opt/cord"
diff --git a/roles/xos-install/tasks/main.yml b/roles/xos-install/tasks/main.yml
deleted file mode 100644
index 846d1a5..0000000
--- a/roles/xos-install/tasks/main.yml
+++ /dev/null
@@ -1,209 +0,0 @@
----
-# tasks for xos-install role
-
-- name: Install prerequisites
-  apt:
-    name={{ item }}
-    update_cache=yes
-    cache_valid_time=3600
-  become: yes
-  with_items:
-   - git
-   - make
-   - curl
-   - python-novaclient
-   - python-neutronclient
-   - python-keystoneclient
-   - python-glanceclient
-
-# ---- copy repos from the dev machine to the head node ----
-# note: this happens in the `cord` repo now
-
-# - name: Create cord destination directory
-#   become: yes
-#   file:
-#     path: "{{ cord_dest_dir }}"
-#     state: directory
-#     mode: 0755
-#     owner: "{{ ansible_user_id }}"
-
-# - name: Copy the whole repo tree
-#   synchronize:
-#       src: "{{ playbook_dir }}/../../../cord/"
-#       dest: "{{ cord_dest_dir }}/"
-
-- name: Create directory xos_services
-  file:
-    path: "{{ ansible_user_dir }}/xos_services"
-    state: directory
-    mode: 0755
-
-- name: Create directory xos_libraries
-  file:
-    path: "{{ ansible_user_dir }}/xos_libraries"
-    state: directory
-    mode: 0755
-
-- name: Create bindings to service-profile and xos
-  become: yes
-  mount:
-      src: "{{ cord_dest_dir }}/orchestration/{{ item }}"
-      name: "{{ ansible_user_dir }}/{{ item }}"
-      fstype: none
-      opts: rw,bind
-      state: mounted
-  with_items:
-      - service-profile
-      - xos
-
-- name: Create bindings for xos services
-  become: yes
-  mount:
-      src: "{{ cord_dest_dir }}/orchestration/xos_services/{{ item }}"
-      name: "{{ ansible_user_dir }}/xos_services/{{ item }}"
-      fstype: none
-      opts: rw,bind
-      state: mounted
-  with_items:
-      - exampleservice
-      - fabric
-      - globalxos
-      - hypercache
-      - metro-net
-      - monitoring
-      - onos-service
-      - openstack
-      - vrouter
-      - vsg
-      - vtr
-
-- name: Create bindings for xos services that reside in onos
-  become: yes
-  mount:
-      src: "{{ cord_dest_dir }}/onos-apps/apps/{{ item }}"
-      name: "{{ ansible_user_dir }}/xos_services/{{ item }}"
-      fstype: none
-      opts: rw,bind
-      state: mounted
-  with_items:
-      - vtn
-      - olt
-
-- name: Create bindings for xos libraries
-  become: yes
-  mount:
-      src: "{{ cord_dest_dir }}/orchestration/xos_libraries/{{ item }}"
-      name: "{{ ansible_user_dir }}/xos_libraries/{{ item }}"
-      fstype: none
-      opts: rw,bind
-      state: mounted
-  with_items:
-      - ng-xos-lib
-
-# ----  alternatively, check out repos from Internet ---
-
-- name: Clone service-profile repo
-  git:
-    repo={{ service_profile_repo_url }}
-    dest={{ service_profile_repo_dest }}
-    version={{ service_profile_repo_branch }}
-    force=yes
-  when:
-    False
-
-# ----  install keys ----
-
-- name: Copy over SSH keys
-  command: cp ~/.ssh/{{ item }} {{ service_profile_repo_dest }}/{{ xos_configuration }}/
-  with_items:
-   - id_rsa
-   - id_rsa.pub
-  tags:
-    - skip_ansible_lint
-
-- name: Copy over node key
-  command: cp {{ ansible_user_dir }}/node_key {{ service_profile_repo_dest }}/{{ xos_configuration }}/
-  tags:
-    - skip_ansible_lint
-
-- name: Set ownership and permissions of keys
-  file:
-    path={{ service_profile_repo_dest }}/{{ xos_configuration }}/{{ item }}
-    owner={{ ansible_user_id }}
-#    mode=0600
-  with_items:
-   - id_rsa
-   - id_rsa.pub
-   - node_key
-
-- name: Copy over core api key
-  copy:
-    src: "{{ playbook_dir }}/pki/intermediate_ca/private/xos-core.{{ site_suffix }}_key.pem"
-    dest: "{{ service_profile_repo_dest }}/{{ xos_configuration }}/core_api_key.pem"
-    mode: 0600
-
-- name: Copy over core api cert
-  copy:
-    src: "{{ playbook_dir }}/pki/intermediate_ca/certs/xos-core.{{ site_suffix }}_cert_chain.pem"
-    dest: "{{ service_profile_repo_dest }}/{{ xos_configuration }}/core_api_cert.pem"
-
-- name: Create templated TOSCA files
-  template:
-    src: "{{ item }}.j2"
-    dest: "{{ service_profile_repo_dest }}/{{ xos_configuration }}/{{ item }}"
-  with_items: "{{ xos_tosca_templates }}"
-
-- name: Download Glance VM images
-  get_url:
-    url={{ item.url }}
-    checksum={{ item.checksum }}
-    dest={{ service_profile_repo_dest }}/{{ xos_configuration }}/images/{{ item.name }}.qcow2
-  with_items: "{{ xos_images }}"
-
-# ---- pull docker images ----
-
-- name: Check to see if registry is reachable
-  command: curl -sf http://docker-registry:5000/
-  ignore_errors: yes
-  register: docker_registry_check
-  tags:
-    - skip_ansible_lint
-
-- name: Use registry if it is available
-  set_fact:
-     docker_registry: "{{ local_docker_registry }}"
-     docker_opts: "--insecure-registry {{ local_docker_registry }}"
-     docker_tag: "candidate"
-  when: docker_registry_check|succeeded
-
-- name: Pull docker images for XOS
-  become: yes
-  command: docker pull {{ docker_registry }}/{{ item }}:{{ docker_tag }}
-  with_items:
-    - xosproject/xos-base
-    - xosproject/xos-postgres
-    - xosproject/cord-app-build
-    - redis
-    - nginx
-    - node
-  tags:
-    - skip_ansible_lint
-
-- name: Tag the images downloaded from the local registry
-  command: docker tag {{ docker_registry }}/{{ item }}:{{ docker_tag }} {{ item }}:latest
-  with_items:
-    - xosproject/xos-base
-    - xosproject/xos-postgres
-    - xosproject/cord-app-build
-    - redis
-    - nginx
-  when: docker_registry_check|succeeded
-
-- name: Separately tag the node image with tag argon
-  command: docker tag {{ docker_registry }}/node:{{ docker_tag }} node:argon
-  when: docker_registry_check|succeeded
-
-
-
-
-
diff --git a/roles/xos-install/templates/nodes.yaml.j2 b/roles/xos-install/templates/nodes.yaml.j2
deleted file mode 100644
index 7ba953b..0000000
--- a/roles/xos-install/templates/nodes.yaml.j2
+++ /dev/null
@@ -1,31 +0,0 @@
-tosca_definitions_version: tosca_simple_yaml_1_0
-
-imports:
-   - custom_types/xos.yaml
-
-description: list of compute nodes, created by platform-install
-
-topology_template:
-  node_templates:
-
-# Site/Deployment, fully defined in deployment.yaml
-    {{ site_name }}:
-      type: tosca.nodes.Site
-
-    {{ deployment_type }}:
-      type: tosca.nodes.Deployment
-
-# compute nodes
-{% for node in groups["compute"] %}
-    {{ hostvars[node]['ansible_hostname'] }}:
-      type: tosca.nodes.Node
-      requirements:
-        - site:
-            node: {{ site_name }}
-            relationship: tosca.relationships.MemberOfSite
-        - deployment:
-            node: {{ deployment_type }}
-            relationship: tosca.relationships.MemberOfDeployment
-
-{% endfor %}
-
diff --git a/roles/xos-install/templates/vtn.yaml.j2 b/roles/xos-install/templates/vtn.yaml.j2
deleted file mode 100644
index f162609..0000000
--- a/roles/xos-install/templates/vtn.yaml.j2
+++ /dev/null
@@ -1,103 +0,0 @@
-tosca_definitions_version: tosca_simple_yaml_1_0
-
-imports:
-   - custom_types/xos.yaml
-
-description: autogenerated node tags file for VTN configuration
-
-topology_template:
-  node_templates:
-
-    service#ONOS_CORD:
-      type: tosca.nodes.ONOSService
-      requirements:
-      properties:
-          kind: onos
-          view_url: /admin/onos/onosservice/$id$/
-          no_container: true
-          rest_hostname: onos-cord
-          rest_port: 8182
-          replaces: service_ONOS_CORD
-
-    service#vtn:
-      type: tosca.nodes.VTNService
-      properties:
-          view_url: /admin/vtn/vtnservice/$id$/
-          privateGatewayMac: 00:00:00:00:00:01
-          localManagementIp: {{ management_network_ip }}
-          ovsdbPort: 6641
-          sshUser: root
-          sshKeyFile: /root/node_key
-          sshPort: 22
-          xosEndpoint: http://xos:8888/
-          xosUser: padmin@vicci.org
-          xosPassword: letmein
-          replaces: service_vtn
-          vtnAPIVersion: 2
-          controllerPort: onos-cord:6654
-
-{% for node in groups["compute"] %}
-{% if 'ipv4' in hostvars[node]['ansible_fabric'] %}
-
-    {{ hostvars[node]['ansible_hostname'] }}:
-      type: tosca.nodes.Node
-
-    # VTN bridgeId field for node {{ hostvars[node]['ansible_hostname'] }}
-    {{ hostvars[node]['ansible_hostname'] }}_bridgeId_tag:
-      type: tosca.nodes.Tag
-      properties:
-          name: bridgeId
-          value: of:0000{{ hostvars[node]['ansible_fabric']['macaddress'] | hwaddr('bare') }}
-      requirements:
-          - target:
-              node: {{ hostvars[node]['ansible_hostname'] }}
-              relationship: tosca.relationships.TagsObject
-          - service:
-              node: service#ONOS_CORD
-              relationship: tosca.relationships.MemberOfService
-
-    # VTN dataPlaneIntf field for node {{ hostvars[node]['ansible_hostname'] }}
-    {{ hostvars[node]['ansible_hostname'] }}_dataPlaneIntf_tag:
-      type: tosca.nodes.Tag
-      properties:
-          name: dataPlaneIntf
-          value: fabric
-      requirements:
-          - target:
-              node: {{ hostvars[node]['ansible_hostname'] }}
-              relationship: tosca.relationships.TagsObject
-          - service:
-              node: service#ONOS_CORD
-              relationship: tosca.relationships.MemberOfService
-
-    # VTN dataPlaneIp field for node {{ hostvars[node]['ansible_hostname'] }}
-    {{ hostvars[node]['ansible_hostname'] }}_dataPlaneIp_tag:
-      type: tosca.nodes.Tag
-      properties:
-          name: dataPlaneIp
-          value: {{ ( hostvars[node]['ansible_fabric']['ipv4']['address'] ~ '/' ~ hostvars[node]['ansible_fabric']['ipv4']['netmask'] ) | ipaddr('cidr') }}
-      requirements:
-          - target:
-              node: {{ hostvars[node]['ansible_hostname'] }}
-              relationship: tosca.relationships.TagsObject
-          - service:
-              node: service#ONOS_CORD
-              relationship: tosca.relationships.MemberOfService
-
-{% endif %}
-{% endfor %}
-
-    VTN_ONOS_app:
-      type: tosca.nodes.ONOSVTNApp
-      requirements:
-          - onos_tenant:
-              node: service#ONOS_CORD
-              relationship: tosca.relationships.TenantOfService
-          - vtn_service:
-              node: service#vtn
-              relationship: tosca.relationships.UsedByService
-      properties:
-          install_dependencies: http://mavenrepo:8080/repository/org/opencord/cord-config/{{ cord_app_version}}/cord-config-{{ cord_app_version }}.oar,http://mavenrepo:8080/repository/org/opencord/vtn/{{ cord_app_version }}/vtn-{{ cord_app_version }}.oar
-          dependencies: org.onosproject.drivers, org.onosproject.drivers.ovsdb, org.onosproject.openflow-base, org.onosproject.ovsdb-base, org.onosproject.dhcp
-          autogenerate: vtn-network-cfg
-
diff --git a/roles/xos-onboard-hosts/defaults/main.yml b/roles/xos-onboard-hosts/defaults/main.yml
new file mode 100644
index 0000000..ee4dbb9
--- /dev/null
+++ b/roles/xos-onboard-hosts/defaults/main.yml
@@ -0,0 +1,7 @@
+---
+# xos-onboard-hosts/defaults/main.yml
+
+cord_dir: "{{ ansible_user_dir + '/cord' }}"
+
+cord_profile_dir: "{{ ansible_user_dir + '/cord_profile' }}"
+
diff --git a/roles/xos-onboard-hosts/tasks/main.yml b/roles/xos-onboard-hosts/tasks/main.yml
new file mode 100644
index 0000000..5adf7d6
--- /dev/null
+++ b/roles/xos-onboard-hosts/tasks/main.yml
@@ -0,0 +1,19 @@
+---
+# xos-onboard-hosts/tasks/main.yml
+
+- name: Get the Docker container names for onboarded containers
+  docker_service:
+    project_name: "{{ cord_profile | regex_replace('\\W','') }}"
+    project_src: "{{ cord_profile_dir }}/onboarding-docker-compose/"
+    recreate: never
+  register: xos_onboard_out
+
+- name: Add the containers to Ansible groups on a per-container type basis
+  add_host:
+    name: "{{ xos_onboard_out.ansible_facts[item].keys() | first }}"
+    groups: "{{ item }}"
+    ansible_connection: "docker"
+    cord_profile: "{{ cord_profile }}"
+    ansible_ssh_user: "root"
+  with_items: "{{ xos_onboard_out.ansible_facts.keys() | list }}"
+
diff --git a/roles/xos-onboarding/defaults/main.yml b/roles/xos-onboarding/defaults/main.yml
new file mode 100644
index 0000000..b9d946f
--- /dev/null
+++ b/roles/xos-onboarding/defaults/main.yml
@@ -0,0 +1,12 @@
+---
+# xos-service-onboard/defaults/main.yml
+
+cord_dir: "{{ ansible_user_dir + '/cord' }}"
+
+xos_bootstrap_ui_port: 9001
+
+xos_libraries:
+  - "ng-xos-lib"
+
+xos_services: []
+
diff --git a/roles/xos-onboarding/tasks/main.yml b/roles/xos-onboarding/tasks/main.yml
new file mode 100644
index 0000000..df3a205
--- /dev/null
+++ b/roles/xos-onboarding/tasks/main.yml
@@ -0,0 +1,86 @@
+---
+# xos-onboarding/tasks/main.yml
+
+- name: Wait for XOS to be ready
+  wait_for:
+    host: localhost
+    port: "{{ xos_bootstrap_ui_port }}"
+    timeout: 120
+
+- name: Bootstrap XOS database - create site, deployment, admin user
+  command: "python /opt/xos/tosca/run.py none /opt/cord_profile/{{ item }}"
+  with_items:
+    - "fixtures.yaml"
+    - "deployment.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Configure XOS with xos.yaml TOSCA
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/xos.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Wait for XOS to be onboarded
+  uri:
+    url: "http://localhost:{{ xos_bootstrap_ui_port }}/api/utility/onboarding/xos/ready/"
+    method: GET
+    return_content: yes
+  register: xos_onboard_status
+  until: '"true" in xos_onboard_status.content'
+  retries: 120
+  delay: 2
+
+- name: Disable onboarding
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/disable-onboarding.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Onboard libraries
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/xos_libraries/{{ item }}/{{ item }}-onboard.yaml"
+  with_items: "{{ xos_libraries }}"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Onboard services
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/xos_services/{{ item.path | basename }}/xos/{{ item.name }}-onboard.yaml"
+  with_items: "{{ xos_services }}"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Enable onboarding
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/enable-onboarding.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+- name: Wait for libraries to be onboarded
+  uri:
+    url: "http://localhost:{{ xos_bootstrap_ui_port }}/api/utility/onboarding/services/{{ item }}/ready/"
+    method: GET
+    return_content: yes
+  register: xos_onboard_status
+  until: '"true" in xos_onboard_status.content'
+  retries: 60
+  delay: 5
+  with_items: "{{ xos_libraries }}"
+
+- name: Wait for services to be onboarded
+  uri:
+    url: "http://localhost:{{ xos_bootstrap_ui_port }}/api/utility/onboarding/services/{{ item.name }}/ready/"
+    method: GET
+    return_content: yes
+  register: xos_onboard_status
+  until: '"true" in xos_onboard_status.content'
+  retries: 60
+  delay: 5
+  with_items: "{{ xos_services }}"
+
+- name: Wait for XOS to be onboarded after service onboarding
+  uri:
+    url: "http://localhost:{{ xos_bootstrap_ui_port }}/api/utility/onboarding/xos/ready/"
+    method: GET
+    return_content: yes
+  register: xos_onboard_status
+  until: '"true" in xos_onboard_status.content'
+  retries: 60
+  delay: 5
+
diff --git a/roles/xos-ready/defaults/main.yml b/roles/xos-ready/defaults/main.yml
new file mode 100644
index 0000000..4dc61e6
--- /dev/null
+++ b/roles/xos-ready/defaults/main.yml
@@ -0,0 +1,4 @@
+---
+# xos-ready/defaults/main.yml
+
+xos_ui_port: 9000
diff --git a/roles/xos-ready/tasks/main.yml b/roles/xos-ready/tasks/main.yml
new file mode 100644
index 0000000..ffdb98c
--- /dev/null
+++ b/roles/xos-ready/tasks/main.yml
@@ -0,0 +1,9 @@
+---
+# xos-ready/tasks/main.yml
+
+- name: Wait for XOS to be ready after service onboarding
+  wait_for:
+    host: localhost
+    port: "{{ xos_ui_port }}"
+    timeout: 60
+
diff --git a/roles/xos-test-restore-db/tasks/main.yml b/roles/xos-test-restore-db/tasks/main.yml
new file mode 100644
index 0000000..05d2518
--- /dev/null
+++ b/roles/xos-test-restore-db/tasks/main.yml
@@ -0,0 +1,28 @@
+---
+# xos-test-restore-db/tasks/main.yml
+
+- name: Restore core initial data from fixture
+  command: python /opt/xos/manage.py --noobserver loaddata /opt/xos/core/fixtures/core_initial_data.json
+  tags:
+    - skip_ansible_lint # testing only
+
+
+- name: Start loading XOS config
+  command: "python /opt/xos/tosca/run.py none /opt/cord_profile/{{ item }}"
+  with_items:
+    - "fixtures.yaml"
+    - "deployment.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+
+
+- name: Continue loading XOS config (as admin user)
+  command: "python /opt/xos/tosca/run.py {{ xos_admin_user }} /opt/cord_profile/{{ item }}"
+  with_items:
+    - "sample.yaml"
+    - "management-net.yaml"
+    - "services.yaml"
+    - "volt-devices.yaml"
+  tags:
+    - skip_ansible_lint # TOSCA loading should be idempotent
+