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
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
|
heat_template_version: 2015-04-30
description: Configure hieradata for Network Cisco configuration
parameters:
# Parameters passed from the parent template
controller_servers:
type: json
compute_servers:
type: json
blockstorage_servers:
type: json
objectstorage_servers:
type: json
cephstorage_servers:
type: json
# extra parameters passed via parameter_defaults
NetworkUCSMIp:
type: string
description: Cisco UCSM IP
default: 127.0.0.1
NetworkUCSMUsername:
type: string
description: Cisco UCSM username
default: admin
NetworkUCSMPassword:
type: string
description: Cisco UCSM password
default: password
NetworkUCSMHostList:
type: string
description: >
Mac address to service profile mapping for UCSM-controlled hosts
The format is
'<host1-mac>:<profile>, <host2-mac>:<profile>, ...'
default: ''
NetworkUCSMSupportedPciDevs:
type: string
description: Cisco UCSM SR-IOV and VM-FEX vendors supported
default: ''
NetworkNexusConfig:
type: json
description: Nexus switch configuration
default: {}
NetworkNexusManagedPhysicalNetwork:
type: string
description: The name of the physical_network
default: ''
NetworkNexusVlanNamePrefix:
type: string
description: A short prefix to prepend to the VLAN name
default: 'q-'
NetworkNexusSviRoundRobin:
type: boolean
description: A flag to enable round robin scheduling
default: false
NetworkNexusProviderVlanNamePrefix:
type: string
description: A short prefix to prepend to the VLAN name
default: 'p-'
NetworkNexusPersistentSwitchConfig:
type: string
description: To make Nexus device persistent
default: false
NetworkNexusSwitchHeartbeatTime:
type: number
description: Time interval to check the state of the Nexus device
default: 0
NetworkNexusSwitchReplayCount:
type: number
description: Number of times to attempt config replay
default: 3
NetworkNexusProviderVlanAutoCreate:
type: boolean
description: A flag whether to manage the creation and removal of VLANs
default: true
NetworkNexusProviderVlanAutoTrunk:
type: boolean
description: A flag whether to manage the trunk ports on the Nexus
default: true
NetworkNexusVxlanGlobalConfig:
type: boolean
description: A flag whether to manage the VXLAN global settings
default: true
NetworkNexusHostKeyChecks:
type: boolean
description: enable strict host key checks when connecting to Nexus switches
default: false
NetworkNexusVxlanVniRanges:
type: string
description: VXLAN Network IDs that are available for tenant network
default: ''
NetworkNexusVxlanMcastRanges:
type: string
description: Multicast groups for the VXLAN interface.
default: ''
resources:
# First we lay down the base configuration via the static hieradata mappings
NetworkCiscoConfig:
type: OS::Heat::StructuredConfig
properties:
group: os-apply-config
config:
hiera:
datafiles:
neutron_cisco_data:
mapped_data:
neutron::plugins::ml2::cisco::ucsm::ucsm_ip: {get_input: UCSM_ip}
neutron::plugins::ml2::cisco::ucsm::ucsm_username: {get_input: UCSM_username}
neutron::plugins::ml2::cisco::ucsm::ucsm_password: {get_input: UCSM_password}
neutron::plugins::ml2::cisco::ucsm::ucsm_host_list: {get_input: UCSM_host_list}
neutron::plugins::ml2::cisco::ucsm::supported_pci_devs: {get_input: UCSMSupportedPciDevs}
neutron::plugins::ml2::cisco::nexus::nexus_config: {get_input: NexusConfig}
neutron::plugins::ml2::cisco::nexus::managed_physical_network: {get_input: NexusManagedPhysicalNetwork}
neutron::plugins::ml2::cisco::nexus::vlan_name_prefix: {get_input: NexusVlanNamePrefix}
neutron::plugins::ml2::cisco::nexus::svi_round_robin: {get_input: NexusSviRoundRobin}
neutron::plugins::ml2::cisco::nexus::provider_vlan_name_prefix: {get_input: NexusProviderVlanNamePrefix}
neutron::plugins::ml2::cisco::nexus::persistent_switch_config: {get_input: NexusPersistentSwitchConfig}
neutron::plugins::ml2::cisco::nexus::switch_heartbeat_time: {get_input: NexusSwitchHeartbeatTime}
neutron::plugins::ml2::cisco::nexus::switch_replay_count: {get_input: NexusSwitchReplayCount}
neutron::plugins::ml2::cisco::nexus::provider_vlan_auto_create: {get_input: NexusProviderVlanAutoCreate}
neutron::plugins::ml2::cisco::nexus::provider_vlan_auto_trunk: {get_input: NexusProviderVlanAutoTrunk}
neutron::plugins::ml2::cisco::nexus::vxlan_global_config: {get_input: NexusVxlanGlobalConfig}
neutron::plugins::ml2::cisco::nexus::host_key_checks: {get_input: NexusHostKeyChecks}
neutron::plugins::ml2::cisco::type_nexus_vxlan::vni_ranges: {get_input: NexusVxlanVniRanges}
neutron::plugins::ml2::cisco::type_nexus_vxlan::mcast_ranges: {get_input: NexusVxlanMcastRanges}
NetworkCiscoDeployment:
type: OS::Heat::StructuredDeployments
properties:
config: {get_resource: NetworkCiscoConfig}
servers: {get_param: controller_servers}
input_values:
UCSM_ip: {get_param: NetworkUCSMIp}
UCSM_username: {get_param: NetworkUCSMUsername}
UCSM_password: {get_param: NetworkUCSMPassword}
UCSM_host_list: {get_attr: [MappingToUCSMDeploymentsController, deploy_stdout]}
UCSMSupportedPciDevs: {get_param: NetworkUCSMSupportedPciDevs}
NexusConfig: {get_attr: [MappingToNexusDeploymentsController, deploy_stdout]}
NexusManagedPhysicalNetwork: {get_param: NetworkNexusManagedPhysicalNetwork}
NexusVlanNamePrefix: {get_param: NetworkNexusVlanNamePrefix}
NexusSviRoundRobin: {get_param: NetworkNexusSviRoundRobin}
NexusProviderVlanNamePrefix: {get_param: NetworkNexusProviderVlanNamePrefix}
NexusPersistentSwitchConfig: {get_param: NetworkNexusPersistentSwitchConfig}
NexusSwitchHeartbeatTime: {get_param: NetworkNexusSwitchHeartbeatTime}
NexusSwitchReplayCount: {get_param: NetworkNexusSwitchReplayCount}
NexusProviderVlanAutoCreate: {get_param: NetworkNexusProviderVlanAutoCreate}
NexusProviderVlanAutoTrunk: {get_param: NetworkNexusProviderVlanAutoTrunk}
NexusVxlanGlobalConfig: {get_param: NetworkNexusVxlanGlobalConfig}
NexusHostKeyChecks: {get_param: NetworkNexusHostKeyChecks}
NexusVxlanVniRanges: {get_param: NetworkNexusVxlanVniRanges}
NexusVxlanMcastRanges: {get_param: NetworkNexusVxlanMcastRanges}
# Now we collect the Mac->Hostname mappings for all nodes, which enables
# calculation of the neutron::plugins::ml2::cisco::nexus::nexus_config data
CollectMacConfig:
type: OS::Heat::SoftwareConfig
properties:
group: script
config: |
#!/bin/sh
MACS=$(ifconfig | grep ether | awk '{print $2}' | tr "\n" " ")
HOST_FQDN=$(hostname -f)
if [ -z "$HOST_FQDN" ]; then
HOSTNAME=$(hostname -s)
# hardcoding the domain name to avoid DNS lookup dependency
# same type of hardcoding appears elsewhere
# --ie. controller-puppet.yaml
# FIXME_HOSTNAME_DOMAIN_HARDCODE
echo "$HOSTNAME.localdomain $MACS"
else
echo "$HOST_FQDN $MACS"
fi
CollectMacDeploymentsController:
type: OS::Heat::SoftwareDeployments
properties:
servers: {get_param: controller_servers}
config: {get_resource: CollectMacConfig}
actions: ['CREATE'] # Only do this on CREATE
CollectMacDeploymentsCompute:
type: OS::Heat::SoftwareDeployments
properties:
servers: {get_param: compute_servers}
config: {get_resource: CollectMacConfig}
actions: ['CREATE'] # Only do this on CREATE
CollectMacDeploymentsBlockStorage:
type: OS::Heat::SoftwareDeployments
properties:
servers: {get_param: blockstorage_servers}
config: {get_resource: CollectMacConfig}
actions: ['CREATE'] # Only do this on CREATE
CollectMacDeploymentsObjectStorage:
type: OS::Heat::SoftwareDeployments
properties:
servers: {get_param: objectstorage_servers}
config: {get_resource: CollectMacConfig}
actions: ['CREATE'] # Only do this on CREATE
CollectMacDeploymentsCephStorage:
type: OS::Heat::SoftwareDeployments
properties:
servers: {get_param: cephstorage_servers}
config: {get_resource: CollectMacConfig}
actions: ['CREATE'] # Only do this on CREATE
# Now we calculate the additional nexus config based on the mappings
MappingToNexusConfig:
type: OS::Heat::SoftwareConfig
properties:
group: script
inputs:
- name: controller_mappings
- name: compute_mappings
- name: blockstorage_mappings
- name: objectstorage_mappings
- name: cephstorage_mappings
- name: nexus_config
config: |
#!/bin/python
import ast
import json
import os
from copy import deepcopy
mappings = ['controller_mappings',
'compute_mappings',
'blockstorage_mappings',
'objectstorage_mappings',
'cephstorage_mappings',
'nexus_config']
mapdict_list = []
nexus = {}
for map_name in mappings:
f_name = '/root/' + map_name
map_data = os.getenv(map_name, "Nada")
with open(f_name, 'a') as f:
f.write(map_data)
if map_data is not "Nada":
if map_name is not 'nexus_config':
mapdict_list.append(ast.literal_eval(map_data))
else:
nexus = ast.literal_eval(map_data)
mac2host = {}
for mapdict in mapdict_list:
for (listnum, host2mac_list) in mapdict.iteritems():
vals = host2mac_list.rstrip().split()
for mac in vals[1:]:
mac2host[mac.lower()] = vals[0]
with open('/root/mac2host', 'a') as f:
f.write(str(mac2host))
# now we have mac to host, map host to switchport in hieradata
# nexus = ast.literal_eval(os.getenv('nexus_config', None))
nexus_cp = deepcopy(nexus)
for nexus_switch in nexus:
for (mac,swport) in nexus[nexus_switch]['servers'].iteritems():
lmac=mac.lower()
if lmac in mac2host:
if mac2host[lmac] in nexus_cp[nexus_switch]['servers']:
nexus_cp[nexus_switch]['servers'][mac2host[lmac]]['ports'] += ',' + swport['ports']
else:
nexus_cp[nexus_switch]['servers'][mac2host[lmac]] = swport
del nexus_cp[nexus_switch]['servers'][mac]
# Note this echo means you can view the data via heat deployment-show
print json.dumps(nexus_cp)
MappingToNexusDeploymentsController:
type: OS::Heat::SoftwareDeployment
properties:
server: {get_param: [controller_servers, '0']}
config: {get_resource: MappingToNexusConfig}
input_values:
# FIXME(shardy): It'd be more convenient if we could join these
# items together but because the returned format is a map (not a list)
# we can't use list_join or str_replace. Possible Heat TODO.
controller_mappings: {get_attr: [CollectMacDeploymentsController, deploy_stdouts]}
compute_mappings: {get_attr: [CollectMacDeploymentsCompute, deploy_stdouts]}
blockstorage_mappings: {get_attr: [CollectMacDeploymentsBlockStorage, deploy_stdouts]}
objectstorage_mappings: {get_attr: [CollectMacDeploymentsObjectStorage, deploy_stdouts]}
cephstorage_mappings: {get_attr: [CollectMacDeploymentsCephStorage, deploy_stdouts]}
nexus_config: {get_param: NetworkNexusConfig}
actions: ['CREATE'] # Only do this on CREATE
MappingToUCSMConfig:
type: OS::Heat::SoftwareConfig
properties:
group: script
inputs:
- name: ucsm_config
config: |
#!/bin/python
import ast
import os
with open('/root/mac2host', 'r') as f:
s=f.read()
m2h=ast.literal_eval(s)
ucs_config = os.getenv('ucsm_config', "Nada")
ucs_data = []
lines = ucs_config.split(',')
for line in lines:
entry=line.rsplit(":",1)
mac = entry[0].lower().strip()
if mac in m2h:
ucs_data.append(m2h[mac] + ":" + entry[1])
print ", ".join(ucs_data)
MappingToUCSMDeploymentsController:
type: OS::Heat::SoftwareDeployment
depends_on: MappingToNexusDeploymentsController
properties:
server: {get_param: [controller_servers, '0']}
config: {get_resource: MappingToUCSMConfig}
input_values:
ucsm_config: {get_param: NetworkUCSMHostList}
actions: ['CREATE'] # Only do this on CREATE
outputs:
# The Deployment applying the hieradata outputs the derived config-id, which
# changes if the input_values change, so if the stdouts from
# NetworkCiscoDeployment change, we need to reapply puppet (which will
# happen if we return a different config_identifier)
config_identifier:
value: {get_attr: [NetworkCiscoDeployment, deploy_stdouts]}
|