1 from __future__
import print_function
# TODO remove when print is no longer needed for debugging
2 from resources
import *
3 from datetime
import datetime
8 import ip_handler
as IP
11 LOG
= logging
.getLogger("api.openstack.heat.parser")
16 The HeatParser will parse a heat dictionary and create a stack and its components, to instantiate it within son-emu.
19 def __init__(self
, compute
):
20 self
.description
= None
21 self
.parameter_groups
= None
22 self
.parameters
= None
25 self
.compute
= compute
26 self
.bufferResource
= list()
28 def parse_input(self
, input_dict
, stack
, dc_label
, stack_update
=False):
30 It will parse the input dictionary into the corresponding classes, which are then stored within the stack.
32 :param input_dict: Dictionary with the template version and resources.
33 :type input_dict: ``dict``
34 :param stack: Reference of the stack that should finally contain all created classes.
35 :type stack: :class:`heat.resources.stack`
36 :param dc_label: String that contains the label of the used data center.
37 :type dc_label: ``str``
38 :param stack_update: Specifies if a new stack will be created or a older one will be updated
39 :type stack_update: ``bool``
40 :return: * *True*: If the template version is supported and all resources could be created.
44 if not self
.check_template_version(str(input_dict
['heat_template_version'])):
45 print('Unsupported template version: ' + input_dict
['heat_template_version'], file=sys
.stderr
)
48 self
.description
= input_dict
.get('description', None)
49 self
.parameter_groups
= input_dict
.get('parameter_groups', None)
50 self
.parameters
= input_dict
.get('parameters', None)
51 self
.resources
= input_dict
.get('resources', None)
52 self
.outputs
= input_dict
.get('outputs', None)
53 # clear bufferResources
54 self
.bufferResource
= list()
56 for resource
in self
.resources
.values():
57 self
.handle_resource(resource
, stack
, dc_label
, stack_update
=stack_update
)
59 # This loop tries to create all classes which had unresolved dependencies.
60 unresolved_resources_last_round
= len(self
.bufferResource
) + 1
61 while len(self
.bufferResource
) > 0 and unresolved_resources_last_round
> len(self
.bufferResource
):
62 unresolved_resources_last_round
= len(self
.bufferResource
)
63 number_of_items
= len(self
.bufferResource
)
64 while number_of_items
> 0:
65 self
.handle_resource(self
.bufferResource
.pop(0), stack
, dc_label
, stack_update
=stack_update
)
68 if len(self
.bufferResource
) > 0:
69 print(str(len(self
.bufferResource
)) +
70 ' classes of the HOT could not be created, because the dependencies could not be found.')
71 print("the problem classes are:")
72 for br
in self
.bufferResource
:
73 print("class: %s" % str(br
))
77 def handle_resource(self
, resource
, stack
, dc_label
, stack_update
=False):
79 This function will take a resource (from a heat template) and determines which type it is and creates
80 the corresponding class, with its required parameters, for further calculations (like deploying the stack).
81 If it is not possible to create the class, because of unresolved dependencies, it will buffer the resource
82 within the 'self.bufferResource' list.
84 :param resource: Dict which contains all important informations about the type and parameters.
85 :type resource: ``dict``
86 :param stack: Reference of the stack that should finally contain the created class.
87 :type stack: :class:`heat.resources.stack`
88 :param dc_label: String that contains the label of the used data center
89 :type dc_label: ``str``
90 :param stack_update: Specifies if a new stack will be created or a older one will be updated
91 :type stack_update: ``bool``
95 if "OS::Neutron::Net" in resource
['type']:
97 net_name
= resource
['properties']['name']
98 if net_name
not in stack
.nets
:
99 stack
.nets
[net_name
] = self
.compute
.create_network(net_name
, True)
101 except Exception as e
:
102 LOG
.warning('Could not create Net: ' + e
.message
)
105 if 'OS::Neutron::Subnet' in resource
['type'] and "Net" not in resource
['type']:
107 net_name
= resource
['properties']['network']['get_resource']
108 if net_name
not in stack
.nets
:
109 net
= self
.compute
.create_network(net_name
, stack_update
)
110 stack
.nets
[net_name
] = net
112 net
= stack
.nets
[net_name
]
114 net
.subnet_name
= resource
['properties']['name']
115 if 'gateway_ip' in resource
['properties']:
116 net
.gateway_ip
= resource
['properties']['gateway_ip']
117 net
.subnet_id
= resource
['properties'].get('id', str(uuid
.uuid4()))
118 net
.subnet_creation_time
= str(datetime
.now())
120 net
.set_cidr(IP
.get_new_cidr(net
.subnet_id
))
121 except Exception as e
:
122 LOG
.warning('Could not create Subnet: ' + e
.message
)
125 if 'OS::Neutron::Port' in resource
['type']:
127 port_name
= resource
['properties']['name']
128 if port_name
not in stack
.ports
:
129 port
= self
.compute
.create_port(port_name
, stack_update
)
130 stack
.ports
[port_name
] = port
132 port
= stack
.ports
[port_name
]
134 if str(resource
['properties']['network']['get_resource']) in stack
.nets
:
135 net
= stack
.nets
[resource
['properties']['network']['get_resource']]
136 if net
.subnet_id
is not None:
137 port
.net_name
= net
.name
138 port
.ip_address
= net
.get_new_ip_address(port
.name
)
140 except Exception as e
:
141 LOG
.warning('Could not create Port: ' + e
.message
)
142 self
.bufferResource
.append(resource
)
145 if 'OS::Nova::Server' in resource
['type']:
147 compute_name
= str(dc_label
) + '_' + str(stack
.stack_name
) + '_' + str(resource
['properties']['name'])
148 shortened_name
= str(dc_label
) + '_' + str(stack
.stack_name
) + '_' + \
149 self
.shorten_server_name(str(resource
['properties']['name']), stack
)
150 nw_list
= resource
['properties']['networks']
152 if shortened_name
not in stack
.servers
:
153 server
= self
.compute
.create_server(shortened_name
, stack_update
)
154 stack
.servers
[shortened_name
] = server
156 server
= stack
.servers
[shortened_name
]
158 server
.full_name
= compute_name
159 server
.template_name
= str(resource
['properties']['name'])
160 server
.command
= resource
['properties'].get('command', '/bin/sh')
161 server
.image
= resource
['properties']['image']
162 server
.flavor
= resource
['properties']['flavor']
165 port_name
= port
['port']['get_resource']
167 # we don't know which network it belongs to yet, but the resource will appear later in a valid
169 if port_name
not in stack
.ports
:
170 stack
.ports
[port_name
] = self
.compute
.create_port(port_name
, stack_update
)
171 server
.port_names
.append(port_name
)
173 except Exception as e
:
174 LOG
.warning('Could not create Server: ' + e
.message
)
177 if 'OS::Neutron::RouterInterface' in resource
['type']:
180 subnet_name
= resource
['properties']['subnet']['get_resource']
182 if 'get_resource' in resource
['properties']['router']:
183 router_name
= resource
['properties']['router']['get_resource']
185 router_name
= resource
['properties']['router']
187 if router_name
not in stack
.routers
:
188 stack
.routers
[router_name
] = Router(router_name
)
190 for tmp_net
in stack
.nets
.values():
191 if tmp_net
.subnet_name
== subnet_name
:
192 stack
.routers
[router_name
].add_subnet(subnet_name
)
194 except Exception as e
:
195 LOG
.warning('Could not create RouterInterface: ' + e
.__repr
__())
196 self
.bufferResource
.append(resource
)
199 if 'OS::Neutron::FloatingIP' in resource
['type']:
201 port_name
= resource
['properties']['port_id']['get_resource']
202 floating_network_id
= resource
['properties']['floating_network_id']
203 if port_name
not in stack
.ports
:
204 stack
.ports
[port_name
] = self
.compute
.create_port(port_name
, stack_update
)
206 stack
.ports
[port_name
].floating_ip
= floating_network_id
207 except Exception as e
:
208 LOG
.warning('Could not create FloatingIP: ' + e
.message
)
211 if 'OS::Neutron::Router' in resource
['type']:
213 name
= resource
['properties']['name']
214 if name
not in stack
.routers
:
215 stack
.routers
[name
] = Router(name
)
216 except Exception as e
:
217 print('Could not create Router: ' + e
.message
)
220 if 'OS::Heat::ResourceGroup' in resource
['type']:
222 embedded_resource
= resource
['properties']['resource_def']
223 LOG
.debug("Found resource in resource group: {}".format(embedded_resource
))
224 # recursively parse embedded resource
225 self
.handle_resource(embedded_resource
, stack
, dc_label
, stack_update
)
226 except Exception as e
:
227 print('Could not create Router: ' + e
.message
)
230 LOG
.warning('Could not determine resource type: {}'.format(resource
['type']))
233 def shorten_server_name(self
, server_name
, stack
):
235 Shortens the server name to a maximum of 12 characters plus the iterator string, if the original name was
238 :param server_name: The original server name.
239 :type server_name: ``str``
240 :param stack: A reference to the used stack.
241 :type stack: :class:`heat.resources.stack`
242 :return: A string with max. 12 characters plus iterator string.
245 server_name
= self
.shorten_name(server_name
, 12)
247 while server_name
in stack
.servers
:
248 server_name
= server_name
[0:12] + str(iterator
)
252 def shorten_name(self
, name
, max_size
):
254 Shortens the name to max_size characters and replaces all '-' with '_'.
256 :param name: The original string.
258 :param max_size: The number of allowed characters.
259 :type max_size: ``int``
260 :return: String with at most max_size characters and without '-'.
263 shortened_name
= name
.split(':', 1)[0]
264 shortened_name
= shortened_name
.replace("-", "_")
265 shortened_name
= shortened_name
[0:max_size
]
266 return shortened_name
268 def check_template_version(self
, version_string
):
270 Checks if a version string is equal or later than 30-04-2015
272 :param version_string: String with the version.
273 :type version_string: ``str``
274 :return: * *True*: if the version is equal or later 30-04-2015.
278 r
= re
.compile('\d{4}-\d{2}-\d{2}')
279 if not r
.match(version_string
):
282 year
, month
, day
= map(int, version_string
.split('-', 2))
288 if month
== 04 and day
< 30: