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 could not be created, because the dependencies could not be found.')
71 def handle_resource(self
, resource
, stack
, dc_label
, stack_update
=False):
73 This function will take a resource (from a heat template) and determines which type it is and creates
74 the corresponding class, with its required parameters, for further calculations (like deploying the stack).
75 If it is not possible to create the class, because of unresolved dependencies, it will buffer the resource
76 within the 'self.bufferResource' list.
78 :param resource: Dict which contains all important informations about the type and parameters.
79 :type resource: ``dict``
80 :param stack: Reference of the stack that should finally contain the created class.
81 :type stack: :class:`heat.resources.stack`
82 :param dc_label: String that contains the label of the used data center
83 :type dc_label: ``str``
84 :param stack_update: Specifies if a new stack will be created or a older one will be updated
85 :type stack_update: ``bool``
89 if "OS::Neutron::Net" in resource
['type']:
91 net_name
= resource
['properties']['name']
92 if net_name
not in stack
.nets
:
93 stack
.nets
[net_name
] = self
.compute
.create_network(net_name
, True)
95 except Exception as e
:
96 logging
.warning('Could not create Net: ' + e
.message
)
99 if 'OS::Neutron::Subnet' in resource
['type'] and "Net" not in resource
['type']:
101 net_name
= resource
['properties']['network']['get_resource']
102 if net_name
not in stack
.nets
:
103 net
= self
.compute
.create_network(net_name
, stack_update
)
104 stack
.nets
[net_name
] = net
106 net
= stack
.nets
[net_name
]
108 net
.subnet_name
= resource
['properties']['name']
109 if 'gateway_ip' in resource
['properties']:
110 net
.gateway_ip
= resource
['properties']['gateway_ip']
111 net
.subnet_id
= resource
['properties'].get('id', str(uuid
.uuid4()))
112 net
.subnet_creation_time
= str(datetime
.now())
114 net
.set_cidr(IP
.get_new_cidr(net
.subnet_id
))
115 except Exception as e
:
116 logging
.warning('Could not create Subnet: ' + e
.message
)
119 if 'OS::Neutron::Port' in resource
['type']:
121 port_name
= resource
['properties']['name']
122 if port_name
not in stack
.ports
:
123 port
= self
.compute
.create_port(port_name
, stack_update
)
124 stack
.ports
[port_name
] = port
126 port
= stack
.ports
[port_name
]
128 if resource
['properties']['network']['get_resource'] in stack
.nets
:
129 net
= stack
.nets
[resource
['properties']['network']['get_resource']]
130 if net
.subnet_id
is not None:
131 port
.net_name
= net
.name
132 port
.ip_address
= net
.get_new_ip_address(port
.name
)
134 except Exception as e
:
135 logging
.warning('Could not create Port: ' + e
.message
)
136 self
.bufferResource
.append(resource
)
139 if 'OS::Nova::Server' in resource
['type']:
141 compute_name
= str(dc_label
) + '_' + str(stack
.stack_name
) + '_' + str(resource
['properties']['name'])
142 shortened_name
= str(dc_label
) + '_' + str(stack
.stack_name
) + '_' + \
143 self
.shorten_server_name(str(resource
['properties']['name']), stack
)
144 nw_list
= resource
['properties']['networks']
146 if shortened_name
not in stack
.servers
:
147 server
= self
.compute
.create_server(shortened_name
, stack_update
)
148 stack
.servers
[shortened_name
] = server
150 server
= stack
.servers
[shortened_name
]
152 server
.full_name
= compute_name
153 server
.template_name
= str(resource
['properties']['name'])
154 server
.command
= resource
['properties'].get('command', '/bin/sh')
155 server
.image
= resource
['properties']['image']
156 server
.flavor
= resource
['properties']['flavor']
159 port_name
= port
['port']['get_resource']
161 # we don't know which network it belongs to yet, but the resource will appear later in a valid
163 if port_name
not in stack
.ports
:
164 stack
.ports
[port_name
] = self
.compute
.create_port(port_name
, stack_update
)
165 server
.port_names
.append(port_name
)
167 except Exception as e
:
168 logging
.warning('Could not create Server: ' + e
.message
)
171 if 'OS::Neutron::RouterInterface' in resource
['type']:
174 subnet_name
= resource
['properties']['subnet']['get_resource']
176 if 'get_resource' in resource
['properties']['router']:
177 router_name
= resource
['properties']['router']['get_resource']
179 router_name
= resource
['properties']['router']
181 if router_name
not in stack
.routers
:
182 stack
.routers
[router_name
] = Router(router_name
)
184 for tmp_net
in stack
.nets
.values():
185 if tmp_net
.subnet_name
== subnet_name
:
186 stack
.routers
[router_name
].add_subnet(subnet_name
)
188 except Exception as e
:
189 logging
.warning('Could not create RouterInterface: ' + e
.__repr
__())
190 self
.bufferResource
.append(resource
)
193 if 'OS::Neutron::FloatingIP' in resource
['type']:
195 port_name
= resource
['properties']['port_id']['get_resource']
196 floating_network_id
= resource
['properties']['floating_network_id']
197 if port_name
not in stack
.ports
:
198 stack
.ports
[port_name
] = self
.compute
.create_port(port_name
, stack_update
)
200 stack
.ports
[port_name
].floating_ip
= floating_network_id
201 except Exception as e
:
202 logging
.warning('Could not create FloatingIP: ' + e
.message
)
205 if 'OS::Neutron::Router' in resource
['type']:
207 name
= resource
['properties']['name']
208 if name
not in stack
.routers
:
209 stack
.routers
[name
] = Router(name
)
210 except Exception as e
:
211 print('Could not create Router: ' + e
.message
)
214 logging
.warning('Could not determine resource type!')
217 def shorten_server_name(self
, server_name
, stack
):
219 Shortens the server name to a maximum of 12 characters plus the iterator string, if the original name was
222 :param server_name: The original server name.
223 :type server_name: ``str``
224 :param stack: A reference to the used stack.
225 :type stack: :class:`heat.resources.stack`
226 :return: A string with max. 12 characters plus iterator string.
229 server_name
= self
.shorten_name(server_name
, 12)
231 while server_name
in stack
.servers
:
232 server_name
= server_name
[0:12] + str(iterator
)
236 def shorten_name(self
, name
, max_size
):
238 Shortens the name to max_size characters and replaces all '-' with '_'.
240 :param name: The original string.
242 :param max_size: The number of allowed characters.
243 :type max_size: ``int``
244 :return: String with at most max_size characters and without '-'.
247 shortened_name
= name
.split(':', 1)[0]
248 shortened_name
= shortened_name
.replace("-", "_")
249 shortened_name
= shortened_name
[0:max_size
]
250 return shortened_name
252 def check_template_version(self
, version_string
):
254 Checks if a version string is equal or later than 30-04-2015
256 :param version_string: String with the version.
257 :type version_string: ``str``
258 :return: * *True*: if the version is equal or later 30-04-2015.
262 r
= re
.compile('\d{4}-\d{2}-\d{2}')
263 if not r
.match(version_string
):
266 year
, month
, day
= map(int, version_string
.split('-', 2))
272 if month
== 04 and day
< 30: