1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
|
#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 7.6 or centos 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
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"
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 }
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"
#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 }
depends_on: [routercon, instance_wait_handle]
#Nfs storage volume for first node.
nfs_storage:
type: OS::Cinder::Volume
properties:
name: nfs_storage
size: 50
#Attachment of volume to first node.
nfs_storage_attachment:
type: OS::Cinder::VolumeAttachment
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 }
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 }
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] }
#Output values
outputs:
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:
yaql:
data:
resources_volid: { get_resource: resources_storage }
nfs_volid: { get_resource: nfs_storage }
docker_volids: { get_attr: [nodes, docker_storage_id] }
#This is going to create a map, where keys are instance names, and values are lists of
#pairs of volume ids and their mount points.
#This is done by merging few generated maps together, base map is taken by
#enumerating over docker storage volumes and transforming them into a map like
#{"node0"=>["volid","/var/lib/docker"],...], node1=>...}
expression: 'dict($.data.docker_volids.enumerate().select(["node"+str($[0]), [[$[1], "/var/lib/docker"]]])).mergeWith({"infra" => [[$.data.resources_volid, "/opt/onap"]], "node0" => [[$.data.nfs_volid, "/dockerdata-nfs"]]})'
|