058fd934c6
Change-Id: I7964b797f967e43384695d4d72b3025e34eadbaa
199 lines
6.4 KiB
Python
199 lines
6.4 KiB
Python
# Copyright (c) 2013 Mirantis Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import eventlet
|
|
import heatclient.client as hclient
|
|
import heatclient.exc as heat_exc
|
|
import keystoneclient.v2_0.client as ksclient
|
|
|
|
|
|
import muranoapi.common.config as config
|
|
|
|
import muranoapi.dsl.helpers as helpers
|
|
import muranoapi.dsl.murano_class as murano_class
|
|
import muranoapi.dsl.murano_object as murano_object
|
|
import muranoapi.openstack.common.log as logging
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
@murano_class.classname('io.murano.system.HeatStack')
|
|
class HeatStack(murano_object.MuranoObject):
|
|
def initialize(self, _context, name):
|
|
self._name = name
|
|
self._template = None
|
|
self._parameters = {}
|
|
self._applied = True
|
|
environment = helpers.get_environment(_context)
|
|
keystone_settings = config.CONF.keystone
|
|
heat_settings = config.CONF.heat
|
|
|
|
client = ksclient.Client(
|
|
endpoint=keystone_settings.auth_url,
|
|
cacert=keystone_settings.ca_file or None,
|
|
cert=keystone_settings.cert_file or None,
|
|
key=keystone_settings.key_file or None,
|
|
insecure=keystone_settings.insecure)
|
|
|
|
if not client.authenticate(
|
|
auth_url=keystone_settings.auth_url,
|
|
tenant_id=environment.tenant_id,
|
|
token=environment.token):
|
|
raise heat_exc.HTTPUnauthorized()
|
|
|
|
heat_url = client.service_catalog.url_for(
|
|
service_type='orchestration',
|
|
endpoint_type=heat_settings.endpoint_type)
|
|
|
|
self._heat_client = hclient.Client(
|
|
'1',
|
|
heat_url,
|
|
username='badusername',
|
|
password='badpassword',
|
|
token_only=True,
|
|
token=client.auth_token,
|
|
ca_file=heat_settings.ca_file or None,
|
|
cert_file=heat_settings.cert_file or None,
|
|
key_file=heat_settings.key_file or None,
|
|
insecure=heat_settings.insecure)
|
|
|
|
def current(self):
|
|
if self._template is not None:
|
|
return self._template
|
|
try:
|
|
stack_info = self._heat_client.stacks.get(stack_id=self._name)
|
|
template = self._heat_client.stacks.template(
|
|
stack_id='{0}/{1}'.format(
|
|
stack_info.stack_name,
|
|
stack_info.id))
|
|
# template = {}
|
|
self._template = template
|
|
self._parameters.update(stack_info.parameters)
|
|
self._applied = True
|
|
return self._template.copy()
|
|
except heat_exc.HTTPNotFound:
|
|
self._applied = True
|
|
self._template = {}
|
|
self._parameters.clear()
|
|
return {}
|
|
|
|
def parameters(self):
|
|
self.current()
|
|
return self._parameters.copy()
|
|
|
|
def reload(self):
|
|
self._template = None
|
|
self._parameters.clear()
|
|
return self.current()
|
|
|
|
def setTemplate(self, template):
|
|
self._template = template
|
|
self._parameters.clear()
|
|
self._applied = False
|
|
|
|
def updateTemplate(self, template):
|
|
self.current()
|
|
self._template = helpers.merge_dicts(self._template, template)
|
|
self._applied = False
|
|
|
|
def _get_status(self):
|
|
status = [None]
|
|
|
|
def status_func(state_value):
|
|
status[0] = state_value
|
|
return True
|
|
|
|
self._wait_state(status_func)
|
|
return status[0]
|
|
|
|
def _wait_state(self, status_func):
|
|
tries = 4
|
|
delay = 1
|
|
while tries > 0:
|
|
while True:
|
|
try:
|
|
stack_info = self._heat_client.stacks.get(
|
|
stack_id=self._name)
|
|
status = stack_info.stack_status
|
|
tries = 4
|
|
delay = 1
|
|
except heat_exc.HTTPNotFound:
|
|
stack_info = None
|
|
status = 'NOT_FOUND'
|
|
except Exception:
|
|
tries -= 1
|
|
delay *= 2
|
|
if not tries:
|
|
raise
|
|
eventlet.sleep(delay)
|
|
break
|
|
|
|
if 'IN_PROGRESS' in status:
|
|
eventlet.sleep(2)
|
|
continue
|
|
if not status_func(status):
|
|
raise EnvironmentError(
|
|
"Unexpected stack state {0}".format(status))
|
|
|
|
try:
|
|
return dict([(t['output_key'], t['output_value'])
|
|
for t in stack_info.outputs])
|
|
except Exception:
|
|
return {}
|
|
return {}
|
|
|
|
def output(self):
|
|
return self._wait_state(lambda: True)
|
|
|
|
def push(self):
|
|
if self._applied or self._template is None:
|
|
return
|
|
|
|
log.info('Pushing: {0}'.format(self._template))
|
|
|
|
current_status = self._get_status()
|
|
if current_status == 'NOT_FOUND':
|
|
if self._template.get('Resources'):
|
|
self._heat_client.stacks.create(
|
|
stack_name=self._name,
|
|
parameters=self._parameters,
|
|
template=self._template,
|
|
disable_rollback=False)
|
|
|
|
self._wait_state(
|
|
lambda status: status == 'CREATE_COMPLETE')
|
|
else:
|
|
if self._template.get('Resources'):
|
|
self._heat_client.stacks.update(
|
|
stack_id=self._name,
|
|
parameters=self._parameters,
|
|
template=self._template)
|
|
self._wait_state(
|
|
lambda status: status == 'UPDATE_COMPLETE')
|
|
else:
|
|
self.delete()
|
|
|
|
self._applied = True
|
|
|
|
def delete(self):
|
|
if not self.current():
|
|
return
|
|
self._heat_client.stacks.delete(
|
|
stack_id=self._name)
|
|
self._wait_state(
|
|
lambda status: status in ('DELETE_COMPLETE', 'NOT_FOUND'))
|
|
self._template = {}
|
|
self._applied = True
|