Merge "Autogenerate missing parameters"

This commit is contained in:
Jenkins 2014-09-23 19:08:38 +00:00 committed by Gerrit Code Review
commit b853c2e26d
6 changed files with 145 additions and 3 deletions

View File

@ -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 = {

View File

@ -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

View File

@ -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.')

View File

@ -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 %}

View File

@ -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(

View File

@ -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)