Ana Krivokapic 6f56762191 Create cinderv2 service and endpoint on the overcloud
Change-Id: I29593d029bc7dd21b22ca75adb7f940333cca0bc
2015-08-21 12:33:16 +02:00

489 lines
18 KiB
Python

# -*- coding: utf8 -*-
#
# 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 logging
import six
import uuid
from django.conf import settings
import django.forms
from django.utils.translation import ugettext_lazy as _
import horizon.exceptions
import horizon.forms
import horizon.messages
from os_cloud_config import keystone as keystone_config
from os_cloud_config.utils import clients
from tuskar_ui import api
import tuskar_ui.api.heat
import tuskar_ui.api.tuskar
import tuskar_ui.forms
import tuskar_ui.infrastructure.flavors.utils as flavors_utils
import tuskar_ui.utils.utils as tuskar_utils
MATCHING_DEPLOYMENT_MODE = flavors_utils.matching_deployment_mode()
LOG = logging.getLogger(__name__)
MESSAGE_ICONS = {
'ok': 'fa-check-square-o text-success',
'pending': 'fa-square-o text-info',
'error': 'fa-exclamation-circle text-danger',
'warning': 'fa-exclamation-triangle text-warning',
None: 'fa-exclamation-triangle text-warning',
}
WEBROOT = getattr(settings, 'WEBROOT', '/')
def validate_roles(request, plan):
"""Validates the roles in plan and returns dict describing the issues"""
for role in plan.role_list:
if (
plan.get_role_node_count(role) and
not role.is_valid_for_deployment(plan)
):
message = {
'text': _(u"Configure Roles."),
'is_critical': True,
'status': 'pending',
}
break
else:
message = {
'text': _(u"Configure Roles."),
'status': 'ok',
}
return message
def validate_global_parameters(request, plan):
pending_required_global_params = list(
api.tuskar.Parameter.pending_parameters(
api.tuskar.Parameter.required_parameters(
api.tuskar.Parameter.global_parameters(
plan.parameter_list()))))
if pending_required_global_params:
message = {
'text': _(u"Global Service Configuration."),
'is_critical': True,
'status': 'pending',
}
else:
message = {
'text': _(u"Global Service Configuration."),
'status': 'ok',
}
return message
def validate_plan(request, plan):
"""Validates the plan and returns a list of dicts describing the issues."""
messages = []
requested_nodes = 0
for role in plan.role_list:
node_count = plan.get_role_node_count(role)
requested_nodes += node_count
available_flavors = len(api.flavor.Flavor.list(request))
if available_flavors == 0:
messages.append({
'text': _(u"Define Flavors."),
'is_critical': True,
'status': 'pending',
})
else:
messages.append({
'text': _(u"Define Flavors."),
'status': 'ok',
})
available_nodes = len(api.node.Node.list(request, associated=False,
maintenance=False))
if available_nodes == 0:
messages.append({
'text': _(u"Register Nodes."),
'is_critical': True,
'status': 'pending',
})
elif requested_nodes > available_nodes:
messages.append({
'text': _(u"Not enough registered nodes for this plan. "
u"You need {0} more.").format(
requested_nodes - available_nodes),
'is_critical': True,
'status': 'error',
})
else:
messages.append({
'text': _(u"Register Nodes."),
'status': 'ok',
})
messages.append(validate_roles(request, plan))
messages.append(validate_global_parameters(request, plan))
if not MATCHING_DEPLOYMENT_MODE:
# All roles have to have the same flavor.
default_flavor_name = api.flavor.Flavor.list(request)[0].name
for role in plan.role_list:
if role.flavor(plan).name != default_flavor_name:
messages.append({
'text': _(u"Role {0} doesn't use default flavor.").format(
role.name,
),
'is_critical': False,
'statis': 'error',
})
roles_assigned = True
messages.append({
'text': _(u"Assign roles."),
'status': lambda: 'ok' if roles_assigned else 'pending',
})
try:
controller_role = plan.get_role_by_name("Controller")
except KeyError:
messages.append({
'text': _(u"Controller Role Needed."),
'is_critical': True,
'status': 'error',
'indent': 1,
})
roles_assigned = False
else:
if plan.get_role_node_count(controller_role) not in (1, 3):
messages.append({
'text': _(u"1 or 3 Controllers Needed."),
'is_critical': True,
'status': 'pending',
'indent': 1,
})
roles_assigned = False
else:
messages.append({
'text': _(u"1 or 3 Controllers Needed."),
'status': 'ok',
'indent': 1,
})
try:
compute_role = plan.get_role_by_name("Compute")
except KeyError:
messages.append({
'text': _(u"Compute Role Needed."),
'is_critical': True,
'status': 'error',
'indent': 1,
})
roles_assigned = False
else:
if plan.get_role_node_count(compute_role) < 1:
messages.append({
'text': _(u"1 Compute Needed."),
'is_critical': True,
'status': 'pending',
'indent': 1,
})
roles_assigned = False
else:
messages.append({
'text': _(u"1 Compute Needed."),
'status': 'ok',
'indent': 1,
})
for message in messages:
status = message.get('status')
if callable(status):
message['status'] = status = status()
message['classes'] = MESSAGE_ICONS.get(status, MESSAGE_ICONS[None])
return messages
class EditPlan(horizon.forms.SelfHandlingForm):
def __init__(self, *args, **kwargs):
super(EditPlan, self).__init__(*args, **kwargs)
self.plan = api.tuskar.Plan.get_the_plan(self.request)
self.fields.update(self._role_count_fields(self.plan))
def _role_count_fields(self, plan):
fields = {}
for role in plan.role_list:
field = django.forms.IntegerField(
label=role.name,
widget=tuskar_ui.forms.NumberPickerInput(attrs={
'min': 1 if role.name in ('Controller', 'Compute') else 0,
'step': 2 if role.name == 'Controller' else 1,
}),
initial=plan.get_role_node_count(role),
required=False
)
field.role = role
fields['%s-count' % role.id] = field
return fields
def handle(self, request, data):
parameters = dict(
(field.role.node_count_parameter_name, data[name])
for (name, field) in self.fields.items() if name.endswith('-count')
)
# NOTE(gfidente): this is a bad hack meant to magically add the
# parameter which enables Neutron L3 HA when the number of
# Controllers is > 1
try:
controller_role = self.plan.get_role_by_name('Controller')
compute_role = self.plan.get_role_by_name('Compute')
except Exception as e:
LOG.warning('Unable to find a required role: %s', e.message)
else:
number_controllers = parameters[
controller_role.node_count_parameter_name]
if number_controllers > 1:
for role in [controller_role, compute_role]:
l3ha_param = role.parameter_prefix + 'NeutronL3HA'
parameters[l3ha_param] = 'True'
l3agent_param = (role.parameter_prefix +
'NeutronAllowL3AgentFailover')
parameters[l3agent_param] = 'True'
dhcp_agents_per_net = (number_controllers if number_controllers and
number_controllers > 3 else 3)
dhcp_agents_param = (controller_role.parameter_prefix +
'NeutronDhcpAgentsPerNetwork')
parameters[dhcp_agents_param] = dhcp_agents_per_net
try:
ceph_storage_role = self.plan.get_role_by_name('Ceph-Storage')
except Exception as e:
LOG.warning('Unable to find role: %s', 'Ceph-Storage')
else:
if parameters[ceph_storage_role.node_count_parameter_name] > 0:
parameters.update({
'CephClusterFSID': six.text_type(uuid.uuid4()),
'CephMonKey': tuskar_utils.create_cephx_key(),
'CephAdminKey': tuskar_utils.create_cephx_key()
})
cinder_enable_rbd_param = (controller_role.parameter_prefix
+ 'CinderEnableRbdBackend')
glance_backend_param = (controller_role.parameter_prefix +
'GlanceBackend')
nova_enable_rbd_param = (compute_role.parameter_prefix +
'NovaEnableRbdBackend')
cinder_enable_iscsi_param = (
controller_role.parameter_prefix +
'CinderEnableIscsiBackend')
parameters.update({
cinder_enable_rbd_param: True,
glance_backend_param: 'rbd',
nova_enable_rbd_param: True,
cinder_enable_iscsi_param: False
})
try:
self.plan = self.plan.patch(request, self.plan.uuid, parameters)
except Exception as e:
horizon.exceptions.handle(request, _("Unable to update the plan."))
LOG.exception(e)
return False
return True
class ScaleOut(EditPlan):
def __init__(self, *args, **kwargs):
super(ScaleOut, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
if name.endswith('-count'):
field.widget.attrs['min'] = field.initial
def handle(self, request, data):
if not super(ScaleOut, self).handle(request, data):
return False
plan = self.plan
try:
stack = api.heat.Stack.get_by_plan(self.request, plan)
stack.update(request, plan.name, plan.templates)
except Exception as e:
LOG.exception(e)
if hasattr(e, 'error'):
horizon.exceptions.handle(
request,
_(
"Unable to deploy overcloud. Reason: {0}"
).format(e.error['error']['message']),
)
return False
else:
raise
else:
msg = _('Deployment in progress.')
horizon.messages.success(request, msg)
return True
class DeployOvercloud(horizon.forms.SelfHandlingForm):
network_isolation = horizon.forms.BooleanField(
label=_("Enable Network Isolation"),
required=False)
def handle(self, request, data):
try:
plan = api.tuskar.Plan.get_the_plan(request)
except Exception as e:
LOG.exception(e)
horizon.exceptions.handle(request,
_("Unable to deploy overcloud."))
return False
# If network isolation selected, read environment file data
# and add to plan
env_temp = '/usr/share/openstack-tripleo-heat-templates/environments'
try:
if self.cleaned_data['network_isolation']:
with open(env_temp, 'r') as env_file:
env_contents = ''.join(
[line for line in
env_file.readlines() if '#' not in line]
)
plan.environment += env_contents
except Exception as e:
LOG.exception(e)
pass
# 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.get('is_critical'):
horizon.messages.success(request, message.text)
return False
try:
stack = api.heat.Stack.get_by_plan(self.request, plan)
if not stack:
api.heat.Stack.create(request, plan.name, plan.templates)
except Exception as e:
LOG.exception(e)
horizon.exceptions.handle(
request, _("Unable to deploy overcloud. Reason: {0}").format(
e.error['error']['message']))
return False
else:
msg = _('Deployment in progress.')
horizon.messages.success(request, msg)
return True
class UndeployOvercloud(horizon.forms.SelfHandlingForm):
def handle(self, request, data):
try:
plan = api.tuskar.Plan.get_the_plan(request)
stack = api.heat.Stack.get_by_plan(self.request, plan)
if stack:
api.heat.Stack.delete(request, stack.id)
except Exception as e:
LOG.exception(e)
horizon.exceptions.handle(request,
_("Unable to undeploy overcloud."))
return False
else:
msg = _('Undeployment in progress.')
horizon.messages.success(request, msg)
return True
class PostDeployInit(horizon.forms.SelfHandlingForm):
admin_email = horizon.forms.CharField(label=_("Admin Email"))
public_host = horizon.forms.CharField(
label=_("Public Host"), initial="", required=False)
region = horizon.forms.CharField(
label=_("Region"), initial="regionOne")
def build_endpoints(self, plan, controller_role):
return {
"ceilometer": {
"password": plan.parameter_value(
controller_role.parameter_prefix + 'CeilometerPassword')},
"cinder": {
"password": plan.parameter_value(
controller_role.parameter_prefix + 'CinderPassword')},
"cinderv2": {
"password": plan.parameter_value(
controller_role.parameter_prefix + 'CinderPassword')},
"ec2": {
"password": plan.parameter_value(
controller_role.parameter_prefix + 'GlancePassword')},
"glance": {
"password": plan.parameter_value(
controller_role.parameter_prefix + 'GlancePassword')},
"heat": {
"password": plan.parameter_value(
controller_role.parameter_prefix + 'HeatPassword')},
"neutron": {
"password": plan.parameter_value(
controller_role.parameter_prefix + 'NeutronPassword')},
"nova": {
"password": plan.parameter_value(
controller_role.parameter_prefix + 'NovaPassword')},
"novav3": {
"password": plan.parameter_value(
controller_role.parameter_prefix + 'NovaPassword')},
"swift": {
"password": plan.parameter_value(
controller_role.parameter_prefix + 'SwiftPassword'),
'path': '/v1/AUTH_%(tenant_id)s',
'admin_path': '/v1'},
"horizon": {
'port': '80',
'path': WEBROOT,
'admin_path': '%sadmin' % WEBROOT}}
def handle(self, request, data):
try:
plan = api.tuskar.Plan.get_the_plan(request)
controller_role = plan.get_role_by_name("Controller")
stack = api.heat.Stack.get_by_plan(self.request, plan)
admin_token = plan.parameter_value(
controller_role.parameter_prefix + 'AdminToken')
admin_password = plan.parameter_value(
controller_role.parameter_prefix + 'AdminPassword')
admin_email = data['admin_email']
auth_ip = stack.keystone_ip
auth_url = stack.keystone_auth_url
auth_tenant = 'admin'
auth_user = 'admin'
# do the keystone init
keystone_config.initialize(
auth_ip, admin_token, admin_email, admin_password,
region='regionOne', ssl=None, public=None, user='heat-admin',
pki_setup=False)
# retrieve needed Overcloud clients
keystone_client = clients.get_keystone_client(
auth_user, admin_password, auth_tenant, auth_url)
# do the setup endpoints
keystone_config.setup_endpoints(
self.build_endpoints(plan, controller_role),
public_host=data['public_host'],
region=data['region'],
os_auth_url=auth_url,
client=keystone_client)
except Exception as e:
LOG.exception(e)
horizon.exceptions.handle(request,
_("Unable to initialize Overcloud."))
return False
else:
msg = _('Overcloud has been initialized.')
horizon.messages.success(request, msg)
return True