Add heat template for k8s-genie

Change-Id: I99b840197098980608b3e18809c8e8003a1c70fd
diff --git a/tcp_tests/templates/heat-cicd-k8s-genie/cookiecutter-context-k8s-genie.yaml b/tcp_tests/templates/heat-cicd-k8s-genie/cookiecutter-context-k8s-genie.yaml
new file mode 100644
index 0000000..48d8f1d
--- /dev/null
+++ b/tcp_tests/templates/heat-cicd-k8s-genie/cookiecutter-context-k8s-genie.yaml
@@ -0,0 +1,128 @@
+default_context:
+  auditd_enabled: 'False'
+  bmk_enabled: 'False'
+  calico_cni_image: docker-prod-local.artifactory.mirantis.com/mirantis/projectcalico/calico/cni:latest
+  calico_enable_nat: 'True'
+  calico_image: docker-prod-local.artifactory.mirantis.com/mirantis/projectcalico/calico/node:latest
+  calico_netmask: '16'
+  calico_network: 192.168.0.0
+  calicoctl_image: docker-prod-local.artifactory.mirantis.com/mirantis/projectcalico/calico/ctl:latest
+  ceph_enabled: 'False'
+  cicd_control_node01_address: ==IPV4_NET_CONTROL_PREFIX==.91
+  cicd_control_node01_hostname: cid01
+  cicd_control_node02_address: ==IPV4_NET_CONTROL_PREFIX==.92
+  cicd_control_node02_hostname: cid02
+  cicd_control_node03_address: ==IPV4_NET_CONTROL_PREFIX==.93
+  cicd_control_node03_hostname: cid03
+  cicd_control_vip_address: ==IPV4_NET_CONTROL_PREFIX==.90
+  cicd_control_vip_hostname: cid
+  cicd_enabled: 'True'
+  cluster_domain: heat-cicd-k8s-genie.local
+  cluster_name: heat-cicd-k8s-genie
+  context_seed: T3sbEdCaBfxrg9ysyA6LIaift250Ktb389rpcISKbdqPi5j0WHKiKAhBftYueBKl
+  control_network_netmask: 255.255.255.0
+  control_network_subnet: ==IPV4_NET_CONTROL_PREFIX==.0/24
+  control_vlan: '10'
+  cookiecutter_template_branch: ''
+  jenkins_pipelines_branch: 'release/2019.2.0'
+  cookiecutter_template_credentials: gerrit
+  cookiecutter_template_url: https://gerrit.mcp.mirantis.com/mk/cookiecutter-templates.git
+  deploy_network_gateway: ==IPV4_NET_ADMIN_PREFIX==.1
+  deploy_network_netmask: 255.255.255.0
+  deploy_network_subnet: ==IPV4_NET_ADMIN_PREFIX==.0/24
+  deployment_type: physical
+  dns_server01: 172.18.176.6
+  dns_server02: 172.18.208.44
+  email_address: ddmitriev@mirantis.com
+  etcd_ssl: 'True'
+  infra_bond_mode: active-backup
+  infra_deploy_nic: eth0
+  infra_kvm01_control_address: ==IPV4_NET_CONTROL_PREFIX==.241
+  infra_kvm01_deploy_address: ==IPV4_NET_ADMIN_PREFIX==.91
+  infra_kvm01_hostname: kvm01
+  infra_kvm02_control_address: ==IPV4_NET_CONTROL_PREFIX==.242
+  infra_kvm02_deploy_address: ==IPV4_NET_ADMIN_PREFIX==.92
+  infra_kvm02_hostname: kvm02
+  infra_kvm03_control_address: ==IPV4_NET_CONTROL_PREFIX==.243
+  infra_kvm03_deploy_address: ==IPV4_NET_ADMIN_PREFIX==.93
+  infra_kvm03_hostname: kvm03
+  infra_kvm_vip_address: ==IPV4_NET_CONTROL_PREFIX==.240
+  infra_primary_first_nic: eth1
+  infra_primary_second_nic: eth2
+  internal_proxy_enabled: 'False'
+  kqueen_custom_mail_enabled: 'False'
+  kqueen_enabled: 'False'
+  kubernetes_control_address: ==IPV4_NET_CONTROL_PREFIX==.10
+  kubernetes_control_node01_address: ==IPV4_NET_CONTROL_PREFIX==.11
+  kubernetes_control_node01_deploy_address: ==IPV4_NET_ADMIN_PREFIX==.11
+  kubernetes_control_node01_hostname: ctl01
+  kubernetes_control_node02_address: ==IPV4_NET_CONTROL_PREFIX==.12
+  kubernetes_control_node02_deploy_address: ==IPV4_NET_ADMIN_PREFIX==.12
+  kubernetes_control_node02_hostname: ctl02
+  kubernetes_control_node03_address: ==IPV4_NET_CONTROL_PREFIX==.13
+  kubernetes_control_node03_deploy_address: ==IPV4_NET_ADMIN_PREFIX==.13
+  kubernetes_control_node03_hostname: ctl03
+  kubernetes_compute_count: 4
+  kubernetes_compute_rack01_hostname: cmp
+  kubernetes_compute_deploy_address_ranges: ==IPV4_NET_ADMIN_PREFIX==.101-==IPV4_NET_ADMIN_PREFIX==.104
+  kubernetes_compute_single_address_ranges: ==IPV4_NET_CONTROL_PREFIX==.101-==IPV4_NET_CONTROL_PREFIX==.104
+  kubernetes_compute_tenant_address_ranges: ==IPV4_NET_TENANT_PREFIX==.101-==IPV4_NET_TENANT_PREFIX==.104
+  kubernetes_enabled: 'True'
+  kubernetes_externaldns_enabled: 'False'
+  kubernetes_keepalived_vip_interface: br_ctl
+  kubernetes_network_calico_enabled: 'True'
+  kubernetes_proxy_hostname: prx
+  kubernetes_proxy_node01_hostname: prx01
+  kubernetes_proxy_node02_hostname: prx02
+  kubernetes_proxy_address: ==IPV4_NET_CONTROL_PREFIX==.220
+  kubernetes_proxy_node01_address: ==IPV4_NET_CONTROL_PREFIX==.221
+  kubernetes_proxy_node02_address: ==IPV4_NET_CONTROL_PREFIX==.222
+  kubernetes_metallb_enabled: 'True'
+  metallb_addresses: 172.17.16.150-172.17.16.190
+  kubernetes_ingressnginx_enabled: 'True'
+  kubernetes_ingressnginx_controller_replicas: 2
+  local_repositories: 'False'
+  maas_deploy_address: ==IPV4_NET_ADMIN_PREFIX==.15
+  maas_deploy_range_end: ==IPV4_NET_ADMIN_PREFIX==.199
+  maas_deploy_range_start: ==IPV4_NET_ADMIN_PREFIX==.180
+  maas_deploy_vlan: '0'
+  maas_fabric_name: deploy-fabric0
+  maas_hostname: cfg01
+  mcp_common_scripts_branch: ''
+  mcp_version: proposed
+  offline_deployment: 'False'
+  opencontrail_enabled: 'False'
+  openldap_domain: ${_param:cluster_name}.local
+  openldap_enabled: 'True'
+  openldap_organisation: ${_param:cluster_name}
+  openssh_groups: cicd
+  openstack_enabled: 'False'
+  oss_enabled: 'False'
+  oss_node03_address: ${_param:stacklight_monitor_node03_address}
+  platform: kubernetes_enabled
+  public_host: ${_param:infra_config_address}
+  publication_method: email
+  reclass_repository: https://github.com/Mirantis/mk-lab-salt-model.git
+  salt_api_password: LTlVnap35hqpRVbB5QjA27EuKh9Ttl3k
+  salt_api_password_hash: $6$RKagUPuQ$Javpjz7b.hqKOOr1rai7uGQd/FnqlOH59tXn12/0G.LkVyunYmgBkSC5zTjoqZvIS1fOOOqsmCb9Q4HcGUbXS.
+  salt_master_address: ==IPV4_NET_CONTROL_PREFIX==.15
+  salt_master_hostname: cfg01
+  salt_master_management_address: ==IPV4_NET_ADMIN_PREFIX==.15
+  shared_reclass_branch: 'proposed'
+  shared_reclass_url: https://gerrit.mcp.mirantis.com/salt-models/reclass-system.git
+  stacklight_enabled: 'False'
+  stacklight_version: '2'
+  static_ips_on_deploy_network_enabled: 'False'
+  tenant_network_gateway: ==IPV4_NET_TENANT_PREFIX==.1
+  tenant_network_netmask: 255.255.255.0
+  tenant_network_subnet: ==IPV4_NET_TENANT_PREFIX==.0/24
+  tenant_vlan: '20'
+  upstream_proxy_enabled: 'False'
+  use_default_network_scheme: 'False'
+  vnf_onboarding_enabled: 'False'
+  kubernetes_network_flannel_enabled: 'True'
+  flannel_network: 10.20.0.0/16
+  kubernetes_network_genie_enabled: 'True'
+  kubernetes_genie_default_plugin: 'calico'
+  kubernetes_virtlet_enabled: 'True'
+  kubernetes_helm_enabled: 'True'
diff --git a/tcp_tests/templates/heat-cicd-k8s-genie/environment-context-k8s-genie.yaml b/tcp_tests/templates/heat-cicd-k8s-genie/environment-context-k8s-genie.yaml
new file mode 100644
index 0000000..d13627b
--- /dev/null
+++ b/tcp_tests/templates/heat-cicd-k8s-genie/environment-context-k8s-genie.yaml
@@ -0,0 +1,145 @@
+nodes:
+    cfg01:
+      reclass_storage_name: infra_config_node01
+      roles:
+      - infra_config
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_static_ctl
+
+    kvm01:
+      reclass_storage_name: infra_kvm_node01
+      roles:
+      - infra_kvm
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl
+
+    kvm02:
+      reclass_storage_name: infra_kvm_node02
+      roles:
+      - infra_kvm
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl
+
+    kvm03:
+      reclass_storage_name: infra_kvm_node03
+      roles:
+      - infra_kvm
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl
+
+    cid01:
+      reclass_storage_name: cicd_control_node01
+      roles:
+      - cicd_control_leader
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl
+
+    cid02:
+      reclass_storage_name: cicd_control_node02
+      roles:
+      - cicd_control_manager
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl
+
+    cid03:
+      reclass_storage_name: cicd_control_node03
+      roles:
+      - cicd_control_manager
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl
+
+    ctl01:
+      reclass_storage_name: kubernetes_control_node01
+      roles:
+      - kubernetes_control
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl_calico
+
+    ctl02:
+      reclass_storage_name: kubernetes_control_node02
+      roles:
+      - kubernetes_control
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl_calico
+
+    ctl03:
+      reclass_storage_name: kubernetes_control_node03
+      roles:
+      - kubernetes_control
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl_calico
+
+    prx01:
+      reclass_storage_name: kubernetes_proxy_node01
+      roles:
+      - kubernetes_proxy
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl
+
+    prx02:
+      reclass_storage_name: kubernetes_proxy_node02
+      roles:
+      - kubernetes_proxy
+      - linux_system_codename_xenial
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl
+
+    # Generator-based computes. For compatibility only
+    cmp<<count>>:
+      reclass_storage_name: kubernetes_compute_rack01
+      roles:
+      - kubernetes_compute
+      - linux_system_codename_xenial
+      - salt_master_host
+      interfaces:
+        ens3:
+          role: single_dhcp
+        ens4:
+          role: single_ctl_calico
diff --git a/tcp_tests/templates/heat-cicd-k8s-genie/salt.yaml b/tcp_tests/templates/heat-cicd-k8s-genie/salt.yaml
new file mode 100644
index 0000000..fbac1ee
--- /dev/null
+++ b/tcp_tests/templates/heat-cicd-k8s-genie/salt.yaml
@@ -0,0 +1,23 @@
+{% set HOSTNAME_CFG01='cfg01.heat-cicd-k8s-genie.local' %}
+{% set LAB_CONFIG_NAME='heat-cicd-k8s-genie' %}
+{% set DOMAIN_NAME='heat-cicd-k8s-genie.local' %}
+{% set SALT_MODELS_REPOSITORY = os_env('SALT_MODELS_REPOSITORY','https://gerrit.mcp.mirantis.com/salt-models/mcp-virtual-lab') %}
+# Other salt model repository parameters see in shared-salt.yaml
+
+{% import 'shared-salt.yaml' as SHARED with context %}
+
+{{ SHARED.MACRO_INSTALL_SALT_MINIONS() }}
+
+{{SHARED.MACRO_CHECK_SALT_VERSION_SERVICES_ON_CFG()}}
+
+{{SHARED.MACRO_CHECK_SALT_VERSION_ON_NODES()}}
+
+- description: "Share custom key from cfg to give each node acces with key from cfg01"
+  cmd: |
+    set -e;
+    set -x;
+    key=$(ssh-keygen -y -f /root/.ssh/id_rsa);
+    salt '*' cmd.run "echo $key >> /root/.ssh/authorized_keys";
+  node_name: {{ HOSTNAME_CFG01 }}
+  retry: {count: 1, delay: 5}
+  skip_fail: true
diff --git a/tcp_tests/templates/heat-cicd-k8s-genie/underlay--user-data-foundation.yaml b/tcp_tests/templates/heat-cicd-k8s-genie/underlay--user-data-foundation.yaml
new file mode 100644
index 0000000..1677dcd
--- /dev/null
+++ b/tcp_tests/templates/heat-cicd-k8s-genie/underlay--user-data-foundation.yaml
@@ -0,0 +1,64 @@
+#cloud-config, see http://cloudinit.readthedocs.io/en/latest/topics/examples.html
+
+ssh_pwauth: True
+users:
+  - name: root
+    sudo: ALL=(ALL) NOPASSWD:ALL
+    shell: /bin/bash
+  - name: jenkins
+    sudo: ALL=(ALL) NOPASSWD:ALL
+    shell: /bin/bash
+    ssh_authorized_keys:
+      - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFSxeuXh2sO4VYL8N2dlNFVyNcr2RvoH4MeDD/cV2HThfU4/BcH6IOOWXSDibIU279bWVKCL7QUp3mf0Vf7HPuyFuC12QM+l7MwT0jCYh5um3hmAvM6Ga0nkhJygHexe9/rLEYzZJkIjP9/IS/YXSv8rhHg484wQ6qkEuq15nyMqil8tbDQCq0XQ+AWEpNpIa4pUoKmFMsOP8lq10KZXIXsJyZxizadr6Bh4Lm9LWrk8YCw7qP3rmgWxK/s8qXQh1ISZe6ONfcmk6p03qbh4H3CwKyWzxmnIHQvE6PgN/O+PuAZj3PbR2mkkJjYX4jNPlxvj8uTStaVPhAwfR9Spdx jenkins@cz8133
+
+disable_root: false
+chpasswd:
+  list: |
+    root:r00tme
+    jenkins:qalab
+  expire: False
+
+packages:
+  - openjdk-8-jre-headless
+  - libyaml-dev
+  - libffi-dev
+  - libvirt-dev
+  - python-dev
+  - python-pip
+  - python-virtualenv
+  #- python-psycopg2
+  - pkg-config
+  - vlan
+  - bridge-utils
+  - ebtables
+
+bootcmd:
+  # Enable root access
+  - sed -i -e '/^PermitRootLogin/s/^.*$/PermitRootLogin yes/' /etc/ssh/sshd_config
+  - service sshd restart
+output:
+  all: '| tee -a /var/log/cloud-init-output.log /dev/tty0'
+
+runcmd:
+  # Create swap
+  - fallocate -l 16G /swapfile
+  - chmod 600 /swapfile
+  - mkswap /swapfile
+  - swapon /swapfile
+  - echo "/swapfile   none    swap    defaults   0   0" >> /etc/fstab
+
+write_files:
+  - path: /etc/default/grub.d/97-enable-grub-menu.cfg
+    content: |
+      GRUB_RECORDFAIL_TIMEOUT=30
+      GRUB_TIMEOUT=3
+      GRUB_TIMEOUT_STYLE=menu
+
+  - path: /etc/network/interfaces
+    content: |
+      auto ens3
+      iface ens3 inet dhcp
+
+  - path: /etc/bash_completion.d/fuel_devops30_activate
+    content: |
+      source /home/jenkins/fuel-devops30/bin/activate
diff --git a/tcp_tests/templates/heat-cicd-k8s-genie/underlay-userdata.yaml b/tcp_tests/templates/heat-cicd-k8s-genie/underlay-userdata.yaml
new file mode 100644
index 0000000..8c1f248
--- /dev/null
+++ b/tcp_tests/templates/heat-cicd-k8s-genie/underlay-userdata.yaml
@@ -0,0 +1,71 @@
+#cloud-config, see http://cloudinit.readthedocs.io/en/latest/topics/examples.html
+
+ssh_pwauth: True
+users:
+   - name: root
+     sudo: ALL=(ALL) NOPASSWD:ALL
+     shell: /bin/bash
+
+disable_root: false
+chpasswd:
+   list: |
+    root:r00tme
+   expire: False
+
+bootcmd:
+   # Enable root access
+   - sed -i -e '/^PermitRootLogin/s/^.*$/PermitRootLogin yes/' /etc/ssh/sshd_config
+   - service sshd restart
+output:
+    all: '| tee -a /var/log/cloud-init-output.log /dev/tty0'
+
+runcmd:
+   - if lvs vg0; then pvresize /dev/vda3; fi
+   - if lvs vg0; then /usr/bin/growlvm.py --image-layout-file /usr/share/growlvm/image-layout.yml; fi
+
+   - export TERM=linux
+   - export LANG=C
+   # Configure dhclient
+   - sudo resolvconf -u
+   #- sudo echo "nameserver {gateway}" >> /etc/resolvconf/resolv.conf.d/base
+   # Enable grub menu using updated config below
+   - update-grub
+
+   # Prepare network connection
+   - sudo ifup ens3
+   #- sudo route add default gw {gateway} {interface_name}
+
+write_files:
+   - path: /etc/default/grub.d/97-enable-grub-menu.cfg
+     content: |
+         GRUB_RECORDFAIL_TIMEOUT=30
+         GRUB_TIMEOUT=3
+         GRUB_TIMEOUT_STYLE=menu
+
+   - path: /etc/network/interfaces
+     content: |
+          auto ens3
+          iface ens3 inet dhcp
+
+   - path: /usr/share/growlvm/image-layout.yml
+     content: |
+       root:
+         size: '65%VG'
+       home:
+         size: '1%VG'
+       var_log:
+         size: '10%VG'
+       var_log_audit:
+         size: '5%VG'
+       var_tmp:
+         size: '10%VG'
+       tmp:
+         size: '5%VG'
+     owner: root:root
+
+growpart:
+    mode: auto
+    devices:
+      - '/'
+      - '/dev/vda3'
+    ignore_growroot_disabled: false
diff --git a/tcp_tests/templates/heat-cicd-k8s-genie/underlay.hot b/tcp_tests/templates/heat-cicd-k8s-genie/underlay.hot
new file mode 100644
index 0000000..a3a1152
--- /dev/null
+++ b/tcp_tests/templates/heat-cicd-k8s-genie/underlay.hot
@@ -0,0 +1,294 @@
+---
+
+heat_template_version: queens
+
+description: MCP environment for heat-cicd-k8s-genie
+
+parameters:
+  instance_domain:
+    type: string
+    default: heat-cicd-k8s-genie.local
+  mcp_version:
+    type: string
+  env_name:
+    type: string
+  control_subnet_cidr:
+    type: string
+  management_subnet_cidr:
+    type: string
+  management_subnet_pool_start:
+    type: string
+  management_subnet_pool_end:
+    type: string
+  management_subnet_cfg01_ip:
+    type: string
+  management_subnet_gateway_ip:
+    type: string
+
+  key_pair:
+    type: string
+
+  ctl_flavor:
+    type: string
+  cfg_flavor:
+    type: string
+  cid_flavor:
+    type: string
+  kvm_fake_flavor:
+    type: string
+  mon_flavor:
+    type: string
+  log_flavor:
+    type: string
+  mtr_flavor:
+    type: string
+  cmp_flavor:
+    type: string
+  foundation_flavor:
+    type: string
+
+  net_public:
+    type: string
+
+resources:
+  networks:
+    type: MCP::Networks
+    properties:
+      stack_name: { get_param: "OS::stack_name" }
+      env_name: { get_param: env_name }
+
+  #flavors:
+  #  type: MCP::Flavors
+
+  cfg01_node:
+    type: MCP::MasterNode
+    depends_on: [networks]
+    properties:
+      env_name: { get_param: env_name }
+      mcp_version: { get_param: mcp_version }
+      cfg01_flavor: { get_param: cfg_flavor }
+      instance_name: cfg01
+      instance_domain: {get_param: instance_domain}
+      network: { get_attr: [networks, network] }
+
+  control_cluster:
+    type: MCP::MultipleInstance
+    depends_on: [cfg01_node]
+    properties:
+      env_name: { get_param: env_name }
+      mcp_version: { get_param: mcp_version }
+      instance_domain: {get_param: instance_domain}
+      instance01_name: ctl01
+      instance02_name: ctl02
+      instance03_name: ctl03
+      instance01_role: [k8s_controller]
+      instance_flavor: {get_param: ctl_flavor}
+      network: { get_attr: [networks, network] }
+      underlay_userdata: { get_file: ./underlay-userdata.yaml }
+      instance01_control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '11' ]
+      instance02_control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '12' ]
+      instance03_control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '13' ]
+
+      instance_config_host: { get_attr: [cfg01_node, instance_address] }
+
+  fake_kvm_cluster:
+    type: MCP::MultipleInstance
+    depends_on: [control_cluster]
+    properties:
+      env_name: { get_param: env_name }
+      mcp_version: { get_param: mcp_version }
+      instance_domain: {get_param: instance_domain}
+      instance01_name: kvm01
+      instance02_name: kvm02
+      instance03_name: kvm03
+      instance_flavor: {get_param: kvm_fake_flavor}
+      network: { get_attr: [networks, network] }
+      underlay_userdata: { get_file: ./underlay-userdata.yaml }
+      instance01_control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '241' ]
+      instance02_control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '242' ]
+      instance03_control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '243' ]
+
+      instance_config_host: { get_attr: [cfg01_node, instance_address] }
+
+  cicd_cluster:
+    type: MCP::MultipleInstance
+    depends_on: [fake_kvm_cluster]
+    properties:
+      env_name: { get_param: env_name }
+      mcp_version: { get_param: mcp_version }
+      instance_domain: {get_param: instance_domain}
+      instance01_name: cid01
+      instance02_name: cid02
+      instance03_name: cid03
+      instance_flavor: {get_param: cid_flavor}
+      network: { get_attr: [networks, network] }
+      underlay_userdata: { get_file: ./underlay-userdata.yaml }
+      instance01_control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '91' ]
+      instance02_control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '92' ]
+      instance03_control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '93' ]
+
+      instance_config_host: { get_attr: [cfg01_node, instance_address] }
+
+  prx01_virtual:
+    type: MCP::SingleInstance
+    depends_on: [cicd_cluster]
+    properties:
+      env_name: { get_param: env_name }
+      mcp_version: { get_param: mcp_version }
+      instance_domain: {get_param: instance_domain}
+      instance_name: prx01
+      instance_flavor: {get_param: cid_flavor}
+      network: { get_attr: [networks, network] }
+      underlay_userdata: { get_file: ./underlay-userdata.yaml }
+      control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '221' ]
+      instance_config_host: { get_attr: [cfg01_node, instance_address] }
+
+  prx02_virtual:
+    type: MCP::SingleInstance
+    depends_on: [prx01_virtual]
+    properties:
+      env_name: { get_param: env_name }
+      mcp_version: { get_param: mcp_version }
+      instance_domain: {get_param: instance_domain}
+      instance_name: prx02
+      instance_flavor: {get_param: cid_flavor}
+      network: { get_attr: [networks, network] }
+      underlay_userdata: { get_file: ./underlay-userdata.yaml }
+      control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '222' ]
+      instance_config_host: { get_attr: [cfg01_node, instance_address] }
+
+  cmp001_virtual:
+    type: MCP::Compute
+    depends_on: [prx02_virtual]
+    properties:
+      env_name: { get_param: env_name }
+      mcp_version: { get_param: mcp_version }
+      instance_domain: {get_param: instance_domain}
+      instance_name: cmp001
+      instance_flavor: {get_param: cmp_flavor}
+      network: { get_attr: [networks, network] }
+      underlay_userdata: { get_file: ./underlay-userdata.yaml }
+      control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '101' ]
+      instance_config_host: { get_attr: [cfg01_node, instance_address] }
+
+  cmp002_virtual:
+    type: MCP::Compute
+    depends_on: [cmp001_virtual]
+    properties:
+      env_name: { get_param: env_name }
+      mcp_version: { get_param: mcp_version }
+      instance_domain: {get_param: instance_domain}
+      instance_name: cmp002
+      instance_flavor: {get_param: cmp_flavor}
+      network: { get_attr: [networks, network] }
+      underlay_userdata: { get_file: ./underlay-userdata.yaml }
+      control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '102' ]
+      instance_config_host: { get_attr: [cfg01_node, instance_address] }
+
+  cmp003_virtual:
+    type: MCP::Compute
+    depends_on: [cmp002_virtual]
+    properties:
+      env_name: { get_param: env_name }
+      mcp_version: { get_param: mcp_version }
+      instance_domain: {get_param: instance_domain}
+      instance_name: cmp003
+      instance_flavor: {get_param: cmp_flavor}
+      network: { get_attr: [networks, network] }
+      underlay_userdata: { get_file: ./underlay-userdata.yaml }
+      control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '103' ]
+      instance_config_host: { get_attr: [cfg01_node, instance_address] }
+
+  cmp004_virtual:
+    type: MCP::Compute
+    depends_on: [cmp003_virtual]
+    properties:
+      env_name: { get_param: env_name }
+      mcp_version: { get_param: mcp_version }
+      instance_domain: {get_param: instance_domain}
+      instance_name: cmp004
+      instance_flavor: {get_param: cmp_flavor}
+      network: { get_attr: [networks, network] }
+      underlay_userdata: { get_file: ./underlay-userdata.yaml }
+      control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '104' ]
+      instance_config_host: { get_attr: [cfg01_node, instance_address] }
+
+  foundation_node:
+    type: MCP::FoundationNode
+    depends_on: [networks]
+    properties:
+      env_name: { get_param: env_name }
+      mcp_version: { get_param: mcp_version }
+      instance_domain: {get_param: instance_domain}
+      instance_name: foundation
+      instance_flavor: {get_param: foundation_flavor}
+      network: { get_attr: [networks, network] }
+      underlay_userdata: { get_file: ./underlay--user-data-foundation.yaml }
+      control_net_static_ip:
+        list_join:
+        - '.'
+        - [ { get_attr: [networks, control_net_prefix] }, '5' ]
+      instance_config_host: { get_attr: [cfg01_node, instance_address] }
+
+outputs:
+
+  control_subnet_cidr:
+    description: Control network CIDR
+    value: { get_param: control_subnet_cidr }
+
+  management_subnet_cidr:
+    description: Admin network CIDR
+    value: { get_param: management_subnet_cidr }
+
+  foundation_floating:
+    description: foundation node IP address (floating) from external network
+    value:
+      get_attr:
+      - foundation_node
+      - instance_floating_address
+...