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
13 The HeatParser will parse a heat dictionary and create a stack and its components, to instantiate it within son-emu.
16 def __init__(self
, compute
):
17 self
.description
= None
18 self
.parameter_groups
= None
19 self
.parameters
= None
22 self
.compute
= compute
23 self
.bufferResource
= list()
25 def parse_input(self
, input_dict
, stack
, dc_label
, stack_update
=False):
27 It will parse the input dictionary into the corresponding classes, which are then stored within the stack.
29 :param input_dict: Dictionary with the template version and resources.
30 :type input_dict: ``dict``
31 :param stack: Reference of the stack that should finally contain all created classes.
32 :type stack: :class:`heat.resources.stack`
33 :param dc_label: String that contains the label of the used data center.
34 :type dc_label: ``str``
35 :param stack_update: Specifies if a new stack will be created or a older one will be updated
36 :type stack_update: ``bool``
37 :return: * *True*: If the template version is supported and all resources could be created.
41 if not self
.check_template_version(str(input_dict
['heat_template_version'])):
42 print('Unsupported template version: ' + input_dict
['heat_template_version'], file=sys
.stderr
)
45 self
.description
= input_dict
.get('description', None)
46 self
.parameter_groups
= input_dict
.get('parameter_groups', None)
47 self
.parameters
= input_dict
.get('parameters', None)
48 self
.resources
= input_dict
.get('resources', None)
49 self
.outputs
= input_dict
.get('outputs', None)
50 # clear bufferResources
51 self
.bufferResource
= list()
53 for resource
in self
.resources
.values():
54 self
.handle_resource(resource
, stack
, dc_label
, stack_update
=stack_update
)
56 # This loop tries to create all classes which had unresolved dependencies.
57 unresolved_resources_last_round
= len(self
.bufferResource
) + 1
58 while len(self
.bufferResource
) > 0 and unresolved_resources_last_round
> len(self
.bufferResource
):
59 unresolved_resources_last_round
= len(self
.bufferResource
)
60 number_of_items
= len(self
.bufferResource
)
61 while number_of_items
> 0:
62 self
.handle_resource(self
.bufferResource
.pop(0), stack
, dc_label
, stack_update
=stack_update
)
65 if len(self
.bufferResource
) > 0:
66 print(str(len(self
.bufferResource
)) +
67 ' classes of the HOT could not be created, because the dependencies could not be found.')
68 print("the problem classes are:")
69 for br
in self
.bufferResource
:
70 print("class: %s" % str(br
))
74 def handle_resource(self
, resource
, stack
, dc_label
, stack_update
=False):
76 This function will take a resource (from a heat template) and determines which type it is and creates
77 the corresponding class, with its required parameters, for further calculations (like deploying the stack).
78 If it is not possible to create the class, because of unresolved dependencies, it will buffer the resource
79 within the 'self.bufferResource' list.
81 :param resource: Dict which contains all important informations about the type and parameters.
82 :type resource: ``dict``
83 :param stack: Reference of the stack that should finally contain the created class.
84 :type stack: :class:`heat.resources.stack`
85 :param dc_label: String that contains the label of the used data center
86 :type dc_label: ``str``
87 :param stack_update: Specifies if a new stack will be created or a older one will be updated
88 :type stack_update: ``bool``
92 if "OS::Neutron::Net" in resource
['type']:
94 net_name
= resource
['properties']['name']
95 if net_name
not in stack
.nets
:
96 stack
.nets
[net_name
] = self
.compute
.create_network(net_name
, True)
98 except Exception as e
:
99 logging
.warning('Could not create Net: ' + e
.message
)
102 if 'OS::Neutron::Subnet' in resource
['type'] and "Net" not in resource
['type']:
104 net_name
= resource
['properties']['network']['get_resource']
105 if net_name
not in stack
.nets
:
106 net
= self
.compute
.create_network(net_name
, stack_update
)
107 stack
.nets
[net_name
] = net
109 net
= stack
.nets
[net_name
]
111 net
.subnet_name
= resource
['properties']['name']
112 if 'gateway_ip' in resource
['properties']:
113 net
.gateway_ip
= resource
['properties']['gateway_ip']
114 net
.subnet_id
= resource
['properties'].get('id', str(uuid
.uuid4()))
115 net
.subnet_creation_time
= str(datetime
.now())
117 net
.set_cidr(IP
.get_new_cidr(net
.subnet_id
))
118 except Exception as e
:
119 logging
.warning('Could not create Subnet: ' + e
.message
)
122 if 'OS::Neutron::Port' in resource
['type']:
124 port_name
= resource
['properties']['name']
125 if port_name
not in stack
.ports
:
126 port
= self
.compute
.create_port(port_name
, stack_update
)
127 stack
.ports
[port_name
] = port
129 port
= stack
.ports
[port_name
]
131 if str(resource
['properties']['network']['get_resource']) in stack
.nets
:
132 net
= stack
.nets
[resource
['properties']['network']['get_resource']]
133 if net
.subnet_id
is not None:
134 port
.net_name
= net
.name
135 port
.ip_address
= net
.get_new_ip_address(port
.name
)
137 except Exception as e
:
138 logging
.warning('Could not create Port: ' + e
.message
)
139 self
.bufferResource
.append(resource
)
142 if 'OS::Nova::Server' in resource
['type']:
144 compute_name
= str(dc_label
) + '_' + str(stack
.stack_name
) + '_' + str(resource
['properties']['name'])
145 shortened_name
= str(dc_label
) + '_' + str(stack
.stack_name
) + '_' + \
146 self
.shorten_server_name(str(resource
['properties']['name']), stack
)
147 nw_list
= resource
['properties']['networks']
149 if shortened_name
not in stack
.servers
:
150 server
= self
.compute
.create_server(shortened_name
, stack_update
)
151 stack
.servers
[shortened_name
] = server
153 server
= stack
.servers
[shortened_name
]
155 server
.full_name
= compute_name
156 server
.template_name
= str(resource
['properties']['name'])
157 server
.command
= resource
['properties'].get('command', '/bin/sh')
158 server
.image
= resource
['properties']['image']
159 server
.flavor
= resource
['properties']['flavor']
162 port_name
= port
['port']['get_resource']
164 # we don't know which network it belongs to yet, but the resource will appear later in a valid
166 if port_name
not in stack
.ports
:
167 stack
.ports
[port_name
] = self
.compute
.create_port(port_name
, stack_update
)
168 server
.port_names
.append(port_name
)
170 except Exception as e
:
171 logging
.warning('Could not create Server: ' + e
.message
)
174 if 'OS::Neutron::RouterInterface' in resource
['type']:
177 subnet_name
= resource
['properties']['subnet']['get_resource']
179 if 'get_resource' in resource
['properties']['router']:
180 router_name
= resource
['properties']['router']['get_resource']
182 router_name
= resource
['properties']['router']
184 if router_name
not in stack
.routers
:
185 stack
.routers
[router_name
] = Router(router_name
)
187 for tmp_net
in stack
.nets
.values():
188 if tmp_net
.subnet_name
== subnet_name
:
189 stack
.routers
[router_name
].add_subnet(subnet_name
)
191 except Exception as e
:
192 logging
.warning('Could not create RouterInterface: ' + e
.__repr
__())
193 self
.bufferResource
.append(resource
)
196 if 'OS::Neutron::FloatingIP' in resource
['type']:
198 port_name
= resource
['properties']['port_id']['get_resource']
199 floating_network_id
= resource
['properties']['floating_network_id']
200 if port_name
not in stack
.ports
:
201 stack
.ports
[port_name
] = self
.compute
.create_port(port_name
, stack_update
)
203 stack
.ports
[port_name
].floating_ip
= floating_network_id
204 except Exception as e
:
205 logging
.warning('Could not create FloatingIP: ' + e
.message
)
208 if 'OS::Neutron::Router' in resource
['type']:
210 name
= resource
['properties']['name']
211 if name
not in stack
.routers
:
212 stack
.routers
[name
] = Router(name
)
213 except Exception as e
:
214 print('Could not create Router: ' + e
.message
)
217 logging
.warning('Could not determine resource type!')
220 def shorten_server_name(self
, server_name
, stack
):
222 Shortens the server name to a maximum of 12 characters plus the iterator string, if the original name was
225 :param server_name: The original server name.
226 :type server_name: ``str``
227 :param stack: A reference to the used stack.
228 :type stack: :class:`heat.resources.stack`
229 :return: A string with max. 12 characters plus iterator string.
232 server_name
= self
.shorten_name(server_name
, 12)
234 while server_name
in stack
.servers
:
235 server_name
= server_name
[0:12] + str(iterator
)
239 def shorten_name(self
, name
, max_size
):
241 Shortens the name to max_size characters and replaces all '-' with '_'.
243 :param name: The original string.
245 :param max_size: The number of allowed characters.
246 :type max_size: ``int``
247 :return: String with at most max_size characters and without '-'.
250 shortened_name
= name
.split(':', 1)[0]
251 shortened_name
= shortened_name
.replace("-", "_")
252 shortened_name
= shortened_name
[0:max_size
]
253 return shortened_name
255 def check_template_version(self
, version_string
):
257 Checks if a version string is equal or later than 30-04-2015
259 :param version_string: String with the version.
260 :type version_string: ``str``
261 :return: * *True*: if the version is equal or later 30-04-2015.
265 r
= re
.compile('\d{4}-\d{2}-\d{2}')
266 if not r
.match(version_string
):
269 year
, month
, day
= map(int, version_string
.split('-', 2))
275 if month
== 04 and day
< 30: