Merge "Autogenerate missing parameters"
This commit is contained in:
commit
b853c2e26d
@ -80,7 +80,6 @@ class Stack(base.APIResourceWrapper):
|
||||
self._request = request
|
||||
|
||||
@classmethod
|
||||
@handle_errors(_("Unable to create Heat stack"), [])
|
||||
def create(cls, request, stack_name, template, environment,
|
||||
provider_resource_templates):
|
||||
fields = {
|
||||
|
@ -11,12 +11,16 @@
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import random
|
||||
import string
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from glanceclient import exc as glance_exceptions
|
||||
from openstack_dashboard.api import base
|
||||
from openstack_dashboard.api import glance
|
||||
from openstack_dashboard.api import neutron
|
||||
from os_cloud_config import keystone_pki
|
||||
from tuskarclient import client as tuskar_client
|
||||
|
||||
from tuskar_ui.api import flavor
|
||||
@ -28,6 +32,11 @@ MASTER_TEMPLATE_NAME = 'plan.yaml'
|
||||
ENVIRONMENT_NAME = 'environment.yaml'
|
||||
TUSKAR_SERVICE = 'management'
|
||||
|
||||
SSL_HIDDEN_PARAMS = ('SSLCertificate', 'SSLKey')
|
||||
KEYSTONE_CERTIFICATE_PARAMS = (
|
||||
'KeystoneSigningCertificate', 'KeystoneCACertificate',
|
||||
'KeystoneSigningKey')
|
||||
|
||||
|
||||
# FIXME: request isn't used right in the tuskar client right now,
|
||||
# but looking at other clients, it seems like it will be in the future
|
||||
@ -50,6 +59,48 @@ def tuskarclient(request, password=None):
|
||||
return client
|
||||
|
||||
|
||||
def password_generator(size=40, chars=(string.ascii_uppercase +
|
||||
string.ascii_lowercase +
|
||||
string.digits)):
|
||||
return ''.join(random.choice(chars) for _ in range(size))
|
||||
|
||||
|
||||
def strip_prefix(parameter_name):
|
||||
return parameter_name.split('::', 1)[-1]
|
||||
|
||||
|
||||
def _is_blank(parameter):
|
||||
return not parameter['value'] or parameter['value'] == 'unset'
|
||||
|
||||
|
||||
def _should_generate_password(parameter):
|
||||
# TODO(lsmola) Filter out SSL params for now. Once it will be generated
|
||||
# in TripleO add it here too. Note: this will also affect how endpoints are
|
||||
# created
|
||||
key = parameter['name']
|
||||
return all([
|
||||
parameter['hidden'],
|
||||
_is_blank(parameter),
|
||||
strip_prefix(key) not in SSL_HIDDEN_PARAMS,
|
||||
strip_prefix(key) not in KEYSTONE_CERTIFICATE_PARAMS,
|
||||
key != 'SnmpdReadonlyUserPassword',
|
||||
])
|
||||
|
||||
|
||||
def _should_generate_keystone_cert(parameter):
|
||||
return all([
|
||||
strip_prefix(parameter['name']) in KEYSTONE_CERTIFICATE_PARAMS,
|
||||
_is_blank(parameter),
|
||||
])
|
||||
|
||||
|
||||
def _should_generate_neutron_control_plane(parameter):
|
||||
return all([
|
||||
strip_prefix(parameter['name']) == 'NeutronControlPlaneID',
|
||||
_is_blank(parameter),
|
||||
])
|
||||
|
||||
|
||||
class Plan(base.APIResourceWrapper):
|
||||
_attrs = ('uuid', 'name', 'description', 'created_at', 'modified_at',
|
||||
'roles', 'parameters')
|
||||
@ -220,6 +271,73 @@ class Plan(base.APIResourceWrapper):
|
||||
return parameter['value']
|
||||
return default
|
||||
|
||||
def list_generated_parameters(self, with_prefix=True):
|
||||
if with_prefix:
|
||||
key_format = lambda key: key
|
||||
else:
|
||||
key_format = strip_prefix
|
||||
|
||||
# Get all password like parameters
|
||||
return dict(
|
||||
(key_format(parameter['name']), parameter)
|
||||
for parameter in self.parameter_list()
|
||||
if any([
|
||||
_should_generate_password(parameter),
|
||||
_should_generate_keystone_cert(parameter),
|
||||
_should_generate_neutron_control_plane(parameter),
|
||||
])
|
||||
)
|
||||
|
||||
def _make_keystone_certificates(self, wanted_generated_params):
|
||||
generated_params = {}
|
||||
for cert_param in KEYSTONE_CERTIFICATE_PARAMS:
|
||||
if cert_param in wanted_generated_params.keys():
|
||||
# If one of the keystone certificates is not set, we have
|
||||
# to generate all of them.
|
||||
generate_certificates = True
|
||||
break
|
||||
else:
|
||||
generate_certificates = False
|
||||
|
||||
# Generate keystone certificates
|
||||
if generate_certificates:
|
||||
ca_key_pem, ca_cert_pem = keystone_pki.create_ca_pair()
|
||||
signing_key_pem, signing_cert_pem = (
|
||||
keystone_pki.create_signing_pair(ca_key_pem, ca_cert_pem))
|
||||
generated_params['KeystoneSigningCertificate'] = (
|
||||
signing_cert_pem)
|
||||
generated_params['KeystoneCACertificate'] = ca_cert_pem
|
||||
generated_params['KeystoneSigningKey'] = signing_key_pem
|
||||
return generated_params
|
||||
|
||||
def make_generated_parameters(self):
|
||||
wanted_generated_params = self.list_generated_parameters(
|
||||
with_prefix=False)
|
||||
|
||||
# Generate keystone certificates
|
||||
generated_params = self._make_keystone_certificates(
|
||||
wanted_generated_params)
|
||||
|
||||
# Generate passwords and control plane id
|
||||
for (key, param) in wanted_generated_params.items():
|
||||
if _should_generate_password(param):
|
||||
generated_params[key] = password_generator()
|
||||
elif _should_generate_neutron_control_plane(param):
|
||||
generated_params[key] = neutron.network_list(
|
||||
self._request, name='ctlplane')[0].id
|
||||
|
||||
# Fill all the Tuskar parameters with generated content. There are
|
||||
# parameters that has just different prefix, such parameters should
|
||||
# have the same values.
|
||||
wanted_prefixed_params = self.list_generated_parameters(
|
||||
with_prefix=True)
|
||||
tuskar_params = {}
|
||||
|
||||
for (key, param) in wanted_prefixed_params.items():
|
||||
tuskar_params[key] = generated_params[strip_prefix(key)]
|
||||
|
||||
return tuskar_params
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self.uuid
|
||||
|
@ -168,6 +168,13 @@ class DeployOvercloud(horizon.forms.SelfHandlingForm):
|
||||
horizon.exceptions.handle(request,
|
||||
_("Unable to deploy overcloud."))
|
||||
return False
|
||||
|
||||
# Auto-generate missing passwords and certificates
|
||||
if plan.list_generated_parameters():
|
||||
generated_params = plan.make_generated_parameters()
|
||||
plan = plan.patch(request, plan.uuid, generated_params)
|
||||
|
||||
# Validate plan and create stack
|
||||
for message in validate_plan(request, plan):
|
||||
if message['is_critical']:
|
||||
horizon.messages.success(request, message.text)
|
||||
@ -182,8 +189,9 @@ class DeployOvercloud(horizon.forms.SelfHandlingForm):
|
||||
plan.provider_resource_templates)
|
||||
except Exception as e:
|
||||
LOG.exception(e)
|
||||
horizon.exceptions.handle(request,
|
||||
_("Unable to deploy overcloud."))
|
||||
horizon.exceptions.handle(
|
||||
request, _("Unable to deploy overcloud. Reason: {0}").format(
|
||||
e.error['error']['message']))
|
||||
return False
|
||||
else:
|
||||
msg = _('Deployment in progress.')
|
||||
|
@ -12,6 +12,10 @@
|
||||
<div>
|
||||
<p>{% trans "You are about deploy your overcloud" %}
|
||||
</p>
|
||||
{% if autogenerated_parameters %}
|
||||
<strong>These parameters will be randomly generated before the deployment:</strong>
|
||||
<p>{{ autogenerated_parameters|join:", " }}</p>
|
||||
{% endif %}
|
||||
<p>{% trans "This operation cannot be undone. Are you sure you want to do that?" %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -55,6 +55,8 @@ def _mock_plan(**kwargs):
|
||||
'parameter_value',
|
||||
'get_role_by_name',
|
||||
'get_role_node_count',
|
||||
'list_generated_parameters',
|
||||
'make_generated_parameters',
|
||||
],
|
||||
'create.side_effect': lambda *args, **kwargs: plan,
|
||||
'delete.return_value': None,
|
||||
@ -67,6 +69,8 @@ def _mock_plan(**kwargs):
|
||||
'parameter_value.return_value': None,
|
||||
'get_role_by_name.side_effect': KeyError,
|
||||
'get_role_node_count.return_value': 0,
|
||||
'list_generated_parameters.return_value': {},
|
||||
'make_generated_parameters.return_value': {},
|
||||
}
|
||||
params.update(kwargs)
|
||||
with patch(
|
||||
|
@ -156,6 +156,15 @@ class DeployConfirmationView(horizon.forms.ModalFormView, StackMixin):
|
||||
form_class = forms.DeployOvercloud
|
||||
template_name = 'infrastructure/overview/deploy_confirmation.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DeployConfirmationView,
|
||||
self).get_context_data(**kwargs)
|
||||
plan = api.tuskar.Plan.get_the_plan(self.request)
|
||||
|
||||
context['autogenerated_parameters'] = (
|
||||
plan.list_generated_parameters(with_prefix=False).keys())
|
||||
return context
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse(INDEX_URL)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user