#This is the environment heat template, compatible with openstack ocata. heat_template_version: 2017-02-24 description: "Heat template for deploying onap env" parameters: auth_key: label: "Auth public key" description: "The public key used to authenticate to instances" type: string node_flavor_name: label: "name of node flavor" description: "The name of the flavor used to create kubernetes nodes" type: string constraints: - custom_constraint: nova.flavor description: "need to specify a valid flavor" infra_flavor_name: label: "name of infra flavor" description: "flavor used to create infra instance" type: string constraints: - custom_constraint: nova.flavor description: "need to specify a valid flavor" installer_flavor_name: label: "name of installer flavor" description: "flavor used to create installer instance" type: string constraints: - custom_constraint: nova.flavor description: "need to specify a valid flavor" image_name: label: "image name" description: "name of the image from which to create all instances, should be rhel/centos 7.9 image" type: string constraints: - custom_constraint: glance.image description: "must specify a valid image name" subnet_cidr: label: "private subnet cidr" description: "Cidr of a private subnet instances will be connected to" type: string constraints: - custom_constraint: net_cidr subnet_range_start: label: "subnet dhcp allocation range start" description: "Start of range of dhcp allocatable ips on private subnet" type: string constraints: - custom_constraint: ip_addr subnet_range_end: label: "end of subnet dhcp allocation range" description: "End of private subnet's dhcp allocation range" type: string constraints: - custom_constraint: ip_addr router_addr: label: "ip address of router" description: "IP address of the router allowing access to other networks incl. company network" type: string constraints: - custom_constraint: ip_addr dns_nameservers: label: "dns resolvers" description: "List of dns resolvers" type: comma_delimited_list public_network_name: label: "name of the public network" description: "Name of the public, internet facing network, also allowing access to company internal hosts" type: string constraints: - custom_constraint: neutron.network description: "Must specify a valid network name or id" external_subnet_cidr: label: "external subnet cidr" description: "The CIDR of the external subnet, that should be accessible from instances, even when internet access is cut. Putting 0.0.0.0/0 here means access to internet." type: string constraints: - custom_constraint: net_cidr installer_ip: label: "floating ip of the installer" description: "a pre-allocated floating ip that will be associated with the installer instance" type: string infra_ip: label: "floating ip of the infra" description: "a pre-allocated floating ip that will be associated with the infrastructure instance" type: string node_ip: label: "floating ip of the first node" description: "a pre-allocated floating ip that will be associated with the first kubernetes node and allow accessing onap" type: string num_nodes: label: "num nodes" description: "the number of kubernetes nodes to create, min 1" type: number constraints: - range: { min: 1 } description: "must be a positive number" use_volume_for_nfs: type: boolean label: "use volume for nfs storage" description: "Indicates whether a cinder volume should be used for nfs storage or not. If not checked, the nfs would be stored in the root disk" demo_network: label: "demo net id" type: string description: "specifies id of network used for demo usecases" default: "" docker_storage_size: label: "nodes' docker storage size" type: number description: "Size of the volume for the docker storage on nodes" conditions: #Condition for nfs volume usage. use_volume_for_nfs: { get_param: use_volume_for_nfs } resources: # Security group used to secure access to instances. secgroup: type: OS::Neutron::SecurityGroup properties: rules: # Egress rule allowing access to external_subnet_cidr. - direction: egress ethertype: IPv4 remote_ip_prefix: { get_param: external_subnet_cidr } # Ingress rule, allowing also inbound access by external network. - direction: ingress ethertype: IPv4 remote_ip_prefix: { get_param: external_subnet_cidr } # Allow outbound communication with the internal subnet. - direction: egress ethertype: IPv4 remote_ip_prefix: { get_param: subnet_cidr } # Allow inbound communication from internal network. - direction: ingress ethertype: IPv4 remote_ip_prefix: { get_param: subnet_cidr } # Allow outbound access to 169.254.0.0/16, mainly for metadata. We do not need inbound. - direction: egress ethertype: IPv4 remote_ip_prefix: 169.254.0.0/16 #A network that our test environment will be connected to. privnet: type: OS::Neutron::Net #Subnet that instances will live in. privsubnet: type: OS::Neutron::Subnet properties: network: { get_resource: privnet } cidr: { get_param: subnet_cidr } allocation_pools: - { start: { get_param: subnet_range_start }, end: { get_param: subnet_range_end } } gateway_ip: { get_param: router_addr } dns_nameservers: { get_param: dns_nameservers } ip_version: 4 #A port connected to the private network, taken by router. routerport: type: OS::Neutron::Port properties: network: { get_resource: privnet } fixed_ips: - { subnet: { get_resource: privsubnet }, ip_address: { get_param: router_addr } } security_groups: [{ get_resource: secgroup }] #This is a router, routing between us and the internet. #It has an external gateway to public network. router: type: OS::Neutron::Router properties: external_gateway_info: network: { get_param: public_network_name } #This is a router interface connecting it to our private subnet's router port. routercon: type: OS::Neutron::RouterInterface properties: router: { get_resource: router } port: { get_resource: routerport } #Key used to authenticate to instances as root. key: type: OS::Nova::KeyPair properties: name: { get_param: "OS::stack_name" } public_key: { get_param: auth_key } #Handle to signal about starting up of instances. instance_wait_handle: type: OS::Heat::WaitConditionHandle #Monitor waiting for all instances to start. instance_wait: type: OS::Heat::WaitCondition properties: handle: { get_resource: instance_wait_handle } timeout: 1200 count: yaql: data: { num_nodes: { get_param: num_nodes } } #This is number of all nodes + 2 (infra instance and installer) expression: "$.data.num_nodes + 2" #Affinity Policy - nodes spread onto as many physical machines as possible (aka. .anti-affinity.). anti_affinity_group: type: OS::Nova::ServerGroup properties: name: k8s nodes on separate computes policies: - anti-affinity #Resource group to deploy n nodes using node template for each, each node numbered starting from 0. nodes: type: OS::Heat::ResourceGroup properties: count: { get_param: num_nodes } resource_def: type: node.yaml properties: nodenum: "%index%" key_name: { get_resource: key } image_name: { get_param: image_name } network: { get_resource: privnet } subnet: { get_resource: privsubnet } flavor_name: { get_param: node_flavor_name } notify_command: { get_attr: ["instance_wait_handle", "curl_cli"] } security_group: { get_resource: secgroup } demo_network: { get_param: demo_network } docker_storage_size: { get_param: docker_storage_size } scheduler_hints: group: { get_resource: anti_affinity_group } depends_on: [routercon, instance_wait_handle] #Nfs storage volume for first node. nfs_storage: type: OS::Cinder::Volume condition: use_volume_for_nfs properties: name: nfs_storage size: 50 #Attachment of volume to first node. nfs_storage_attachment: type: OS::Cinder::VolumeAttachment condition: use_volume_for_nfs properties: instance_uuid: { get_attr: [nodes, "resource.0"] } volume_id: { get_resource: nfs_storage } #Floating ip association for node (first only). node_fip_assoc: type: OS::Neutron::FloatingIPAssociation properties: floatingip_id: { get_param: node_ip } port_id: { get_attr: ["nodes", "resource.0.port_id"] } #Openstack volume used for storing resources. resources_storage: type: "OS::Cinder::Volume" properties: name: "resources_storage" size: 120 #Instance representing infrastructure instance, created using subtemplate. infra: type: "instance.yaml" properties: instance_name: infra network: { get_resource: privnet } subnet: { get_resource: privsubnet } key_name: { get_resource: key } flavor_name: { get_param: infra_flavor_name } image_name: { get_param: image_name } notify_command: { get_attr: ["instance_wait_handle", "curl_cli"] } security_group: { get_resource: secgroup } scheduler_hints: {} demo_network: { get_param: demo_network } depends_on: [instance_wait_handle] #Volume attachment for infra node. resources_storage_attachment: type: OS::Cinder::VolumeAttachment properties: volume_id: { get_resource: resources_storage } instance_uuid: { get_resource: infra } #Floating ip association for infra. infra_fip_assoc: type: OS::Neutron::FloatingIPAssociation properties: floatingip_id: { get_param: infra_ip } port_id: { get_attr: ["infra", "port_id"] } #Small installer vm having access to other instances, used to install onap. installer: type: "instance.yaml" properties: instance_name: installer image_name: { get_param: image_name } flavor_name: { get_param: installer_flavor_name } key_name: { get_resource: key } network: { get_resource: privnet } subnet: { get_resource: privsubnet } notify_command: { get_attr: ["instance_wait_handle", "curl_cli"] } security_group: { get_resource: secgroup } scheduler_hints: {} depends_on: instance_wait_handle #Floating ip for installer. installer_fip_assoc: type: OS::Neutron::FloatingIPAssociation properties: floatingip_id: { get_param: installer_ip } port_id: { get_attr: [installer, port_id] } #Map of node volumes, taken from volumes output param. node_volumes: type: OS::Heat::Value properties: type: json #We need yaql transformation to be done on the volume map. value: yaql: data: #This is a map of node number to value of "volumes" attribute, that contains #a list of volumes written as pairs [volumeid, mountpoint]. volumes: { get_attr: [nodes, attributes, volumes] } #We need yaql expressions to transform node numbers to node names in the form "node0" and similar. #However we don't need anything more complicated. expression: "$.data.volumes?.items()?.toDict('node'+str($[0]), $[1])" #List of infra specific volumes (not a map as above). infra_volumes: type: OS::Heat::Value properties: value: - [{ get_resource: resources_storage }, "/opt/onap"] #Contains node0 specific volume list. node0_volumes: type: OS::Heat::Value properties: #Note that it returns an empty list if nfs volume is disabled. value: if: - use_volume_for_nfs - - [{ get_resource: nfs_storage }, "/dockerdata-nfs"] - [] #Output values outputs: network_name: value: {get_attr: [privnet, name] } description: "Name of private network" network_id: value: { get_resource: privnet } description: "ID of private network" subnet_id: value: { get_resource: privsubnet } description: "ID of private subnet" installer_ip: value: { get_attr: [installer, ip] } description: "Internal ip of installer instance" infra_ip: value: { get_attr: [infra, ip] } description: "Internal ip of infra instance" node_ips: value: { get_attr: [nodes, ip] } description: "Serialized json list of node internal ips starting at node0" volumes: description: "map of volumes per each instance" value: #Can do deep merging only with yaql. yaql: data: node_volumes: { get_attr: [node_volumes, value]} infra_volumes: { infra: { get_attr: [infra_volumes, value] }} node0_volumes: {node0: { get_attr: [node0_volumes, value] }} expression: "$.data.node_volumes?.mergeWith($.data.infra_volumes)?.mergeWith($.data.node0_volumes)"