Add handle get_file when launch stack from horizon
when get_file is contained in template, the stack create/update/preview will fail due to No content found in the "files" section. So added handle get_file code. Change-Id: I6f125f9e5f3f53f630ab0d4f3f00631e6850e905 Closes-Bug: #1512564
This commit is contained in:
parent
184a1e6810
commit
d1c3b4787b
@ -170,6 +170,26 @@ class AlreadyExists(HorizonException):
|
|||||||
return self.msg % self.attrs
|
return self.msg % self.attrs
|
||||||
|
|
||||||
|
|
||||||
|
@six.python_2_unicode_compatible
|
||||||
|
class GetFileError(HorizonException):
|
||||||
|
"""Exception to be raised when the value of get_file did not start with
|
||||||
|
https:// or http://
|
||||||
|
"""
|
||||||
|
def __init__(self, name, resource_type):
|
||||||
|
self.attrs = {"name": name, "resource": resource_type}
|
||||||
|
self.msg = _('The value of %(resource)s is %(name)s inside the '
|
||||||
|
'template. When launching a stack from this interface,'
|
||||||
|
' the value must start with "http://" or "https://"')
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<%s name=%r resource_type=%r>' % (self.__class__.__name__,
|
||||||
|
self.attrs['name'],
|
||||||
|
self.attrs['resource_type'])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.msg % self.attrs
|
||||||
|
|
||||||
|
|
||||||
class ConfigurationError(HorizonException):
|
class ConfigurationError(HorizonException):
|
||||||
"""Exception to be raised when invalid settings have been provided."""
|
"""Exception to be raised when invalid settings have been provided."""
|
||||||
pass
|
pass
|
||||||
@ -203,6 +223,7 @@ class HandledException(HorizonException):
|
|||||||
UNAUTHORIZED = tuple(HORIZON_CONFIG['exceptions']['unauthorized'])
|
UNAUTHORIZED = tuple(HORIZON_CONFIG['exceptions']['unauthorized'])
|
||||||
UNAUTHORIZED += (NotAuthorized,)
|
UNAUTHORIZED += (NotAuthorized,)
|
||||||
NOT_FOUND = tuple(HORIZON_CONFIG['exceptions']['not_found'])
|
NOT_FOUND = tuple(HORIZON_CONFIG['exceptions']['not_found'])
|
||||||
|
NOT_FOUND += (GetFileError,)
|
||||||
RECOVERABLE = (AlreadyExists, Conflict, NotAvailable, ServiceCatalogException)
|
RECOVERABLE = (AlreadyExists, Conflict, NotAvailable, ServiceCatalogException)
|
||||||
RECOVERABLE += tuple(HORIZON_CONFIG['exceptions']['recoverable'])
|
RECOVERABLE += tuple(HORIZON_CONFIG['exceptions']['recoverable'])
|
||||||
|
|
||||||
|
@ -10,9 +10,18 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from django.conf import settings
|
import contextlib
|
||||||
from heatclient import client as heat_client
|
import six
|
||||||
|
from six.moves.urllib import request
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from oslo_serialization import jsonutils
|
||||||
|
|
||||||
|
from heatclient import client as heat_client
|
||||||
|
from heatclient.common import template_format
|
||||||
|
from heatclient.common import template_utils
|
||||||
|
from heatclient.common import utils as heat_utils
|
||||||
|
from horizon import exceptions
|
||||||
from horizon.utils import functions as utils
|
from horizon.utils import functions as utils
|
||||||
from horizon.utils.memoized import memoized # noqa
|
from horizon.utils.memoized import memoized # noqa
|
||||||
from openstack_dashboard.api import base
|
from openstack_dashboard.api import base
|
||||||
@ -82,6 +91,57 @@ def stacks_list(request, marker=None, sort_dir='desc', sort_key='created_at',
|
|||||||
return (stacks, has_more_data, has_prev_data)
|
return (stacks, has_more_data, has_prev_data)
|
||||||
|
|
||||||
|
|
||||||
|
def _ignore_if(key, value):
|
||||||
|
if key != 'get_file' and key != 'type':
|
||||||
|
return True
|
||||||
|
if not isinstance(value, six.string_types):
|
||||||
|
return True
|
||||||
|
if (key == 'type' and
|
||||||
|
not value.endswith(('.yaml', '.template'))):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_template_files(template_data=None, template_url=None):
|
||||||
|
if template_data:
|
||||||
|
tpl = template_data
|
||||||
|
elif template_url:
|
||||||
|
with contextlib.closing(request.urlopen(template_url)) as u:
|
||||||
|
tpl = u.read()
|
||||||
|
else:
|
||||||
|
return {}, None
|
||||||
|
if not tpl:
|
||||||
|
return {}, None
|
||||||
|
if isinstance(tpl, six.binary_type):
|
||||||
|
tpl = tpl.decode('utf-8')
|
||||||
|
template = template_format.parse(tpl)
|
||||||
|
files = {}
|
||||||
|
_get_file_contents(template, files)
|
||||||
|
return files, template
|
||||||
|
|
||||||
|
|
||||||
|
def _get_file_contents(from_data, files):
|
||||||
|
if not isinstance(from_data, (dict, list)):
|
||||||
|
return
|
||||||
|
if isinstance(from_data, dict):
|
||||||
|
recurse_data = six.itervalues(from_data)
|
||||||
|
for key, value in six.iteritems(from_data):
|
||||||
|
if _ignore_if(key, value):
|
||||||
|
continue
|
||||||
|
if not value.startswith(('http://', 'https://')):
|
||||||
|
raise exceptions.GetFileError(value, 'get_file')
|
||||||
|
if value not in files:
|
||||||
|
file_content = heat_utils.read_url_content(value)
|
||||||
|
if template_utils.is_template(file_content):
|
||||||
|
template = get_template_files(template_url=value)[1]
|
||||||
|
file_content = jsonutils.dumps(template)
|
||||||
|
files[value] = file_content
|
||||||
|
else:
|
||||||
|
recurse_data = from_data
|
||||||
|
for value in recurse_data:
|
||||||
|
_get_file_contents(value, files)
|
||||||
|
|
||||||
|
|
||||||
def stack_delete(request, stack_id):
|
def stack_delete(request, stack_id):
|
||||||
return heatclient(request).stacks.delete(stack_id)
|
return heatclient(request).stacks.delete(stack_id)
|
||||||
|
|
||||||
|
@ -139,17 +139,18 @@ class TemplateForm(forms.SelfHandlingForm):
|
|||||||
|
|
||||||
# Validate the template and get back the params.
|
# Validate the template and get back the params.
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if cleaned['template_data']:
|
|
||||||
kwargs['template'] = cleaned['template_data']
|
|
||||||
else:
|
|
||||||
kwargs['template_url'] = cleaned['template_url']
|
|
||||||
|
|
||||||
if cleaned['environment_data']:
|
if cleaned['environment_data']:
|
||||||
kwargs['environment'] = cleaned['environment_data']
|
kwargs['environment'] = cleaned['environment_data']
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
files, tpl =\
|
||||||
|
api.heat.get_template_files(cleaned.get('template_data'),
|
||||||
|
cleaned.get('template_url'))
|
||||||
|
kwargs['files'] = files
|
||||||
|
kwargs['template'] = tpl
|
||||||
validated = api.heat.template_validate(self.request, **kwargs)
|
validated = api.heat.template_validate(self.request, **kwargs)
|
||||||
cleaned['template_validate'] = validated
|
cleaned['template_validate'] = validated
|
||||||
|
cleaned['template_validate']['files'] = files
|
||||||
|
cleaned['template_validate']['template'] = tpl
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise forms.ValidationError(six.text_type(e))
|
raise forms.ValidationError(six.text_type(e))
|
||||||
|
|
||||||
@ -209,9 +210,7 @@ class TemplateForm(forms.SelfHandlingForm):
|
|||||||
|
|
||||||
def create_kwargs(self, data):
|
def create_kwargs(self, data):
|
||||||
kwargs = {'parameters': data['template_validate'],
|
kwargs = {'parameters': data['template_validate'],
|
||||||
'environment_data': data['environment_data'],
|
'environment_data': data['environment_data']}
|
||||||
'template_data': data['template_data'],
|
|
||||||
'template_url': data['template_url']}
|
|
||||||
if data.get('stack_id'):
|
if data.get('stack_id'):
|
||||||
kwargs['stack_id'] = data['stack_id']
|
kwargs['stack_id'] = data['stack_id']
|
||||||
return kwargs
|
return kwargs
|
||||||
@ -250,12 +249,6 @@ class CreateStackForm(forms.SelfHandlingForm):
|
|||||||
class Meta(object):
|
class Meta(object):
|
||||||
name = _('Create Stack')
|
name = _('Create Stack')
|
||||||
|
|
||||||
template_data = forms.CharField(
|
|
||||||
widget=forms.widgets.HiddenInput,
|
|
||||||
required=False)
|
|
||||||
template_url = forms.CharField(
|
|
||||||
widget=forms.widgets.HiddenInput,
|
|
||||||
required=False)
|
|
||||||
environment_data = forms.CharField(
|
environment_data = forms.CharField(
|
||||||
widget=forms.widgets.HiddenInput,
|
widget=forms.widgets.HiddenInput,
|
||||||
required=False)
|
required=False)
|
||||||
@ -375,15 +368,12 @@ class CreateStackForm(forms.SelfHandlingForm):
|
|||||||
'timeout_mins': data.get('timeout_mins'),
|
'timeout_mins': data.get('timeout_mins'),
|
||||||
'disable_rollback': not(data.get('enable_rollback')),
|
'disable_rollback': not(data.get('enable_rollback')),
|
||||||
'parameters': dict(params_list),
|
'parameters': dict(params_list),
|
||||||
|
'files': json.loads(data.get('parameters')).get('files'),
|
||||||
|
'template': json.loads(data.get('parameters')).get('template')
|
||||||
}
|
}
|
||||||
if data.get('password'):
|
if data.get('password'):
|
||||||
fields['password'] = data.get('password')
|
fields['password'] = data.get('password')
|
||||||
|
|
||||||
if data.get('template_data'):
|
|
||||||
fields['template'] = data.get('template_data')
|
|
||||||
else:
|
|
||||||
fields['template_url'] = data.get('template_url')
|
|
||||||
|
|
||||||
if data.get('environment_data'):
|
if data.get('environment_data'):
|
||||||
fields['environment'] = data.get('environment_data')
|
fields['environment'] = data.get('environment_data')
|
||||||
|
|
||||||
@ -430,19 +420,12 @@ class EditStackForm(CreateStackForm):
|
|||||||
'timeout_mins': data.get('timeout_mins'),
|
'timeout_mins': data.get('timeout_mins'),
|
||||||
'disable_rollback': not(data.get('enable_rollback')),
|
'disable_rollback': not(data.get('enable_rollback')),
|
||||||
'parameters': dict(params_list),
|
'parameters': dict(params_list),
|
||||||
|
'files': json.loads(data.get('parameters')).get('files'),
|
||||||
|
'template': json.loads(data.get('parameters')).get('template')
|
||||||
}
|
}
|
||||||
if data.get('password'):
|
if data.get('password'):
|
||||||
fields['password'] = data.get('password')
|
fields['password'] = data.get('password')
|
||||||
|
|
||||||
# if the user went directly to this form, resubmit the existing
|
|
||||||
# template data. otherwise, submit what they had from the first form
|
|
||||||
if data.get('template_data'):
|
|
||||||
fields['template'] = data.get('template_data')
|
|
||||||
elif data.get('template_url'):
|
|
||||||
fields['template_url'] = data.get('template_url')
|
|
||||||
elif data.get('parameters'):
|
|
||||||
fields['template'] = data.get('parameters')
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
api.heat.stack_update(self.request, stack_id=stack_id, **fields)
|
api.heat.stack_update(self.request, stack_id=stack_id, **fields)
|
||||||
messages.success(request, _("Stack update started."))
|
messages.success(request, _("Stack update started."))
|
||||||
@ -469,13 +452,10 @@ class PreviewStackForm(CreateStackForm):
|
|||||||
'timeout_mins': data.get('timeout_mins'),
|
'timeout_mins': data.get('timeout_mins'),
|
||||||
'disable_rollback': not(data.get('enable_rollback')),
|
'disable_rollback': not(data.get('enable_rollback')),
|
||||||
'parameters': dict(params_list),
|
'parameters': dict(params_list),
|
||||||
|
'files': json.loads(data.get('parameters')).get('files'),
|
||||||
|
'template': json.loads(data.get('parameters')).get('template')
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.get('template_data'):
|
|
||||||
fields['template'] = data.get('template_data')
|
|
||||||
else:
|
|
||||||
fields['template_url'] = data.get('template_url')
|
|
||||||
|
|
||||||
if data.get('environment_data'):
|
if data.get('environment_data'):
|
||||||
fields['environment'] = data.get('environment_data')
|
fields['environment'] = data.get('environment_data')
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ from django.utils import html
|
|||||||
from mox3.mox import IsA # noqa
|
from mox3.mox import IsA # noqa
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from heatclient.common import template_format as hc_format
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.test import helpers as test
|
from openstack_dashboard.test import helpers as test
|
||||||
|
|
||||||
@ -230,16 +231,18 @@ class StackTests(test.TestCase):
|
|||||||
stack = self.stacks.first()
|
stack = self.stacks.first()
|
||||||
|
|
||||||
api.heat.template_validate(IsA(http.HttpRequest),
|
api.heat.template_validate(IsA(http.HttpRequest),
|
||||||
template=template.data) \
|
files={},
|
||||||
|
template=hc_format.parse(template.data)) \
|
||||||
.AndReturn(json.loads(template.validate))
|
.AndReturn(json.loads(template.validate))
|
||||||
|
|
||||||
api.heat.stack_create(IsA(http.HttpRequest),
|
api.heat.stack_create(IsA(http.HttpRequest),
|
||||||
stack_name=stack.stack_name,
|
stack_name=stack.stack_name,
|
||||||
timeout_mins=60,
|
timeout_mins=60,
|
||||||
disable_rollback=True,
|
disable_rollback=True,
|
||||||
template=template.data,
|
template=None,
|
||||||
parameters=IsA(dict),
|
parameters=IsA(dict),
|
||||||
password='password')
|
password='password',
|
||||||
|
files=None)
|
||||||
api.neutron.network_list_for_tenant(IsA(http.HttpRequest),
|
api.neutron.network_list_for_tenant(IsA(http.HttpRequest),
|
||||||
self.tenant.id) \
|
self.tenant.id) \
|
||||||
.AndReturn(self.networks.list())
|
.AndReturn(self.networks.list())
|
||||||
@ -287,7 +290,8 @@ class StackTests(test.TestCase):
|
|||||||
stack = self.stacks.first()
|
stack = self.stacks.first()
|
||||||
|
|
||||||
api.heat.template_validate(IsA(http.HttpRequest),
|
api.heat.template_validate(IsA(http.HttpRequest),
|
||||||
template=template.data,
|
files={},
|
||||||
|
template=hc_format.parse(template.data),
|
||||||
environment=environment.data) \
|
environment=environment.data) \
|
||||||
.AndReturn(json.loads(template.validate))
|
.AndReturn(json.loads(template.validate))
|
||||||
|
|
||||||
@ -295,10 +299,11 @@ class StackTests(test.TestCase):
|
|||||||
stack_name=stack.stack_name,
|
stack_name=stack.stack_name,
|
||||||
timeout_mins=60,
|
timeout_mins=60,
|
||||||
disable_rollback=True,
|
disable_rollback=True,
|
||||||
template=template.data,
|
template=None,
|
||||||
environment=environment.data,
|
environment=environment.data,
|
||||||
parameters=IsA(dict),
|
parameters=IsA(dict),
|
||||||
password='password')
|
password='password',
|
||||||
|
files=None)
|
||||||
api.neutron.network_list_for_tenant(IsA(http.HttpRequest),
|
api.neutron.network_list_for_tenant(IsA(http.HttpRequest),
|
||||||
self.tenant.id) \
|
self.tenant.id) \
|
||||||
.AndReturn(self.networks.list())
|
.AndReturn(self.networks.list())
|
||||||
@ -371,7 +376,8 @@ class StackTests(test.TestCase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
api.heat.template_validate(IsA(http.HttpRequest),
|
api.heat.template_validate(IsA(http.HttpRequest),
|
||||||
template=template['data']) \
|
files={},
|
||||||
|
template=hc_format.parse(template['data'])) \
|
||||||
.AndReturn(template['validate'])
|
.AndReturn(template['validate'])
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
@ -448,7 +454,8 @@ class StackTests(test.TestCase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
api.heat.template_validate(IsA(http.HttpRequest),
|
api.heat.template_validate(IsA(http.HttpRequest),
|
||||||
template=template['data']) \
|
files={},
|
||||||
|
template=hc_format.parse(template['data'])) \
|
||||||
.AndReturn(template['validate'])
|
.AndReturn(template['validate'])
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
@ -522,20 +529,22 @@ class StackTests(test.TestCase):
|
|||||||
stack = self.stacks.first()
|
stack = self.stacks.first()
|
||||||
|
|
||||||
api.heat.template_validate(IsA(http.HttpRequest),
|
api.heat.template_validate(IsA(http.HttpRequest),
|
||||||
template=template['data']) \
|
files={},
|
||||||
|
template=hc_format.parse(template['data'])) \
|
||||||
.AndReturn(template['validate'])
|
.AndReturn(template['validate'])
|
||||||
|
|
||||||
api.heat.stack_create(IsA(http.HttpRequest),
|
api.heat.stack_create(IsA(http.HttpRequest),
|
||||||
stack_name=stack.stack_name,
|
stack_name=stack.stack_name,
|
||||||
timeout_mins=60,
|
timeout_mins=60,
|
||||||
disable_rollback=True,
|
disable_rollback=True,
|
||||||
template=template['data'],
|
template=hc_format.parse(template['data']),
|
||||||
parameters={'param1': 'some string',
|
parameters={'param1': 'some string',
|
||||||
'param2': 42,
|
'param2': 42,
|
||||||
'param3': '{"key": "value"}',
|
'param3': '{"key": "value"}',
|
||||||
'param4': 'a,b,c',
|
'param4': 'a,b,c',
|
||||||
'param5': True},
|
'param5': True},
|
||||||
password='password')
|
password='password',
|
||||||
|
files={})
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@ -612,7 +621,8 @@ class StackTests(test.TestCase):
|
|||||||
stack.id).AndReturn(stack)
|
stack.id).AndReturn(stack)
|
||||||
# POST template form, validation
|
# POST template form, validation
|
||||||
api.heat.template_validate(IsA(http.HttpRequest),
|
api.heat.template_validate(IsA(http.HttpRequest),
|
||||||
template=template.data) \
|
files={},
|
||||||
|
template=hc_format.parse(template.data)) \
|
||||||
.AndReturn(json.loads(template.validate))
|
.AndReturn(json.loads(template.validate))
|
||||||
|
|
||||||
# GET to edit form
|
# GET to edit form
|
||||||
@ -631,8 +641,9 @@ class StackTests(test.TestCase):
|
|||||||
'disable_rollback': True,
|
'disable_rollback': True,
|
||||||
'timeout_mins': 61,
|
'timeout_mins': 61,
|
||||||
'password': 'password',
|
'password': 'password',
|
||||||
'template': IsA(six.text_type),
|
'template': None,
|
||||||
'parameters': IsA(dict)
|
'parameters': IsA(dict),
|
||||||
|
'files': None
|
||||||
}
|
}
|
||||||
api.heat.stack_update(IsA(http.HttpRequest),
|
api.heat.stack_update(IsA(http.HttpRequest),
|
||||||
stack_id=stack.id,
|
stack_id=stack.id,
|
||||||
@ -755,15 +766,17 @@ class StackTests(test.TestCase):
|
|||||||
stack = self.stacks.first()
|
stack = self.stacks.first()
|
||||||
|
|
||||||
api.heat.template_validate(IsA(http.HttpRequest),
|
api.heat.template_validate(IsA(http.HttpRequest),
|
||||||
template=template.data) \
|
files={},
|
||||||
|
template=hc_format.parse(template.data)) \
|
||||||
.AndReturn(json.loads(template.validate))
|
.AndReturn(json.loads(template.validate))
|
||||||
|
|
||||||
api.heat.stack_preview(IsA(http.HttpRequest),
|
api.heat.stack_preview(IsA(http.HttpRequest),
|
||||||
stack_name=stack.stack_name,
|
stack_name=stack.stack_name,
|
||||||
timeout_mins=60,
|
timeout_mins=60,
|
||||||
disable_rollback=True,
|
disable_rollback=True,
|
||||||
template=template.data,
|
template=None,
|
||||||
parameters=IsA(dict)).AndReturn(stack)
|
parameters=IsA(dict),
|
||||||
|
files=None).AndReturn(stack)
|
||||||
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
|
@ -179,19 +179,12 @@ class CreateStackView(forms.ModalFormView):
|
|||||||
|
|
||||||
def get_initial(self):
|
def get_initial(self):
|
||||||
initial = {}
|
initial = {}
|
||||||
self.load_kwargs(initial)
|
if 'environment_data' in self.kwargs:
|
||||||
|
initial['environment_data'] = self.kwargs['environment_data']
|
||||||
if 'parameters' in self.kwargs:
|
if 'parameters' in self.kwargs:
|
||||||
initial['parameters'] = json.dumps(self.kwargs['parameters'])
|
initial['parameters'] = json.dumps(self.kwargs['parameters'])
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
def load_kwargs(self, initial):
|
|
||||||
# load the "passed through" data from template form
|
|
||||||
for prefix in ('template', 'environment'):
|
|
||||||
for suffix in ('_data', '_url'):
|
|
||||||
key = prefix + suffix
|
|
||||||
if key in self.kwargs:
|
|
||||||
initial[key] = self.kwargs[key]
|
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
def get_form_kwargs(self):
|
||||||
kwargs = super(CreateStackView, self).get_form_kwargs()
|
kwargs = super(CreateStackView, self).get_form_kwargs()
|
||||||
if 'parameters' in self.kwargs:
|
if 'parameters' in self.kwargs:
|
||||||
|
@ -9,10 +9,12 @@
|
|||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
import six
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.test.utils import override_settings # noqa
|
from django.test.utils import override_settings # noqa
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
from openstack_dashboard import api
|
from openstack_dashboard import api
|
||||||
from openstack_dashboard.test import helpers as test
|
from openstack_dashboard.test import helpers as test
|
||||||
|
|
||||||
@ -211,3 +213,84 @@ class HeatApiTests(test.APITestCase):
|
|||||||
**form_data)
|
**form_data)
|
||||||
from heatclient.v1 import stacks
|
from heatclient.v1 import stacks
|
||||||
self.assertIsInstance(returned_stack, stacks.Stack)
|
self.assertIsInstance(returned_stack, stacks.Stack)
|
||||||
|
|
||||||
|
def test_get_template_files_with_template_data(self):
|
||||||
|
tmpl = '''
|
||||||
|
heat_template_version: 2013-05-23
|
||||||
|
resources:
|
||||||
|
server1:
|
||||||
|
type: OS::Nova::Server
|
||||||
|
properties:
|
||||||
|
flavor: m1.medium
|
||||||
|
image: cirros
|
||||||
|
'''
|
||||||
|
expected_files = {}
|
||||||
|
files = api.heat.get_template_files(template_data=tmpl)[0]
|
||||||
|
self.assertEqual(files, expected_files)
|
||||||
|
|
||||||
|
def test_get_template_files(self):
|
||||||
|
tmpl = '''
|
||||||
|
heat_template_version: 2013-05-23
|
||||||
|
resources:
|
||||||
|
server1:
|
||||||
|
type: OS::Nova::Server
|
||||||
|
properties:
|
||||||
|
flavor: m1.medium
|
||||||
|
image: cirros
|
||||||
|
user_data_format: RAW
|
||||||
|
user_data:
|
||||||
|
get_file: http://test.example/example
|
||||||
|
'''
|
||||||
|
expected_files = {u'http://test.example/example': b'echo "test"'}
|
||||||
|
url = 'http://test.example/example'
|
||||||
|
data = b'echo "test"'
|
||||||
|
self.mox.StubOutWithMock(six.moves.urllib.request, 'urlopen')
|
||||||
|
six.moves.urllib.request.urlopen(url).AndReturn(
|
||||||
|
six.BytesIO(data))
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
files = api.heat.get_template_files(template_data=tmpl)[0]
|
||||||
|
self.assertEqual(files, expected_files)
|
||||||
|
|
||||||
|
def test_get_template_files_with_template_url(self):
|
||||||
|
url = 'https://test.example/example.yaml'
|
||||||
|
data = b'''
|
||||||
|
heat_template_version: 2013-05-23
|
||||||
|
resources:
|
||||||
|
server1:
|
||||||
|
type: OS::Nova::Server
|
||||||
|
properties:
|
||||||
|
flavor: m1.medium
|
||||||
|
image: cirros
|
||||||
|
user_data_format: RAW
|
||||||
|
user_data:
|
||||||
|
get_file: http://test.example/example
|
||||||
|
'''
|
||||||
|
url2 = 'http://test.example/example'
|
||||||
|
data2 = b'echo "test"'
|
||||||
|
expected_files = {'http://test.example/example': b'echo "test"'}
|
||||||
|
self.mox.StubOutWithMock(six.moves.urllib.request, 'urlopen')
|
||||||
|
six.moves.urllib.request.urlopen(url).AndReturn(
|
||||||
|
six.BytesIO(data))
|
||||||
|
six.moves.urllib.request.urlopen(url2).AndReturn(
|
||||||
|
six.BytesIO(data2))
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
files = api.heat.get_template_files(template_url=url)[0]
|
||||||
|
self.assertEqual(files, expected_files)
|
||||||
|
|
||||||
|
def test_get_template_files_invalid(self):
|
||||||
|
tmpl = '''
|
||||||
|
heat_template_version: 2013-05-23
|
||||||
|
resources:
|
||||||
|
server1:
|
||||||
|
type: OS::Nova::Server
|
||||||
|
properties:
|
||||||
|
flavor: m1.medium
|
||||||
|
image: cirros
|
||||||
|
user_data_format: RAW
|
||||||
|
user_data:
|
||||||
|
get_file: file:///example
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
api.heat.get_template_files(template_data=tmpl)[0]
|
||||||
|
except exceptions.GetFileError:
|
||||||
|
self.assertRaises(exceptions.GetFileError)
|
||||||
|
Loading…
Reference in New Issue
Block a user