
E127/E128 checks helps us to keep code more readable, so these rules should be allowed, when pep8 runs. Enabled check E128 - continuation line under-indented for visual indent. Change-Id: Ic17a7ba47d555183ff26a5dc5f6ec27c0461725a
265 lines
9.6 KiB
Python
265 lines
9.6 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# 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.
|
|
|
|
from django.core import urlresolvers
|
|
from django.utils.translation import ugettext_lazy as _ # noqa
|
|
|
|
from horizon import exceptions
|
|
from horizon import forms
|
|
from horizon import messages
|
|
from horizon import workflows
|
|
|
|
import requests
|
|
|
|
from tuskar_ui import api as tuskar
|
|
from tuskar_ui import forms as tuskar_forms
|
|
from tuskar_ui.infrastructure.resource_management.nodes import tables \
|
|
as nodes_tables
|
|
import tuskar_ui.workflows
|
|
|
|
|
|
class RackCreateInfoAction(workflows.Action):
|
|
name = forms.RegexField(label=_("Name"),
|
|
max_length=25,
|
|
regex=r'^[\w\.\- ]+$',
|
|
error_messages={'invalid': _(
|
|
'Name may only '
|
|
'contain letters, numbers, underscores, '
|
|
'periods and hyphens.')})
|
|
location = forms.CharField(label=_("Location"))
|
|
subnet = tuskar_forms.NetworkField(label=_("IP Subnet"))
|
|
resource_class_id = forms.ChoiceField(label=_("Resource Class"))
|
|
|
|
def clean(self):
|
|
cleaned_data = super(RackCreateInfoAction, self).clean()
|
|
name = cleaned_data.get('name')
|
|
rack_id = self.initial.get('rack_id', None)
|
|
subnet = cleaned_data.get('subnet')
|
|
try:
|
|
racks = tuskar.Rack.list(self.request)
|
|
except Exception:
|
|
racks = []
|
|
exceptions.check_message(['Connection', 'refused'],
|
|
_("Unable to retrieve rack list."))
|
|
raise
|
|
|
|
# Validations: detect duplicates
|
|
for rack in racks:
|
|
other_record = rack_id != rack.id
|
|
if rack.name == name and other_record:
|
|
raise forms.ValidationError(
|
|
_('The name %s is already used by another rack.')
|
|
% name)
|
|
if rack.subnet == subnet and other_record:
|
|
raise forms.ValidationError(
|
|
_('The subnet is already assigned to rack %s.')
|
|
% (rack.name))
|
|
|
|
return cleaned_data
|
|
|
|
def __init__(self, request, *args, **kwargs):
|
|
super(RackCreateInfoAction, self).__init__(request, *args, **kwargs)
|
|
resource_class_id_choices = [('', _("Select a Resource Class"))]
|
|
for rc in tuskar.ResourceClass.list(request):
|
|
resource_class_id_choices.append((rc.id, rc.name))
|
|
self.fields['resource_class_id'].choices = resource_class_id_choices
|
|
|
|
class Meta:
|
|
name = _("Rack Settings")
|
|
|
|
|
|
class CreateRackInfo(workflows.Step):
|
|
action_class = RackCreateInfoAction
|
|
|
|
contributes = ('name', 'resource_class_id', 'subnet', 'location')
|
|
|
|
def get_racks_data():
|
|
pass
|
|
|
|
|
|
class EditRackInfo(CreateRackInfo):
|
|
depends_on = ('rack_id',)
|
|
|
|
|
|
class NodeCreateAction(workflows.Action):
|
|
class Meta:
|
|
name = _("Create Nodes")
|
|
help_text = _("Here you can create the nodes for this rack.")
|
|
|
|
def clean(self):
|
|
cleaned_data = super(NodeCreateAction, self).clean()
|
|
table = self.initial.get('_tables', {}).get('nodes')
|
|
if table:
|
|
formset = table.get_formset()
|
|
if formset.is_valid():
|
|
cleaned_data['nodes'] = [form.cleaned_data
|
|
for form in formset
|
|
if form.cleaned_data
|
|
and not
|
|
form.cleaned_data.get('DELETE')]
|
|
else:
|
|
raise forms.ValidationError(_("Errors in the nodes list."))
|
|
return cleaned_data
|
|
|
|
|
|
class NodeEditAction(NodeCreateAction):
|
|
class Meta:
|
|
name = _("Edit Nodes")
|
|
help_text = _("Here you can edit the nodes for this rack.")
|
|
|
|
|
|
class CreateNodes(tuskar_ui.workflows.TableStep):
|
|
action_class = NodeCreateAction
|
|
contributes = ('nodes',)
|
|
table_classes = (nodes_tables.NodesFormsetTable,)
|
|
template_name = (
|
|
'infrastructure/resource_management/racks/_rack_nodes_step.html')
|
|
|
|
def get_nodes_data(self):
|
|
return []
|
|
|
|
|
|
class EditNodes(CreateNodes):
|
|
action_class = NodeEditAction
|
|
depends_on = ('rack_id',)
|
|
|
|
def get_nodes_data(self):
|
|
rack_id = self.workflow.context['rack_id']
|
|
rack = tuskar.Rack.get(self.workflow.request, rack_id)
|
|
return rack.list_baremetal_nodes
|
|
|
|
|
|
class CreateRack(workflows.Workflow):
|
|
default_steps = (CreateRackInfo, CreateNodes)
|
|
slug = "create_rack"
|
|
name = _("Add Rack")
|
|
success_url = 'horizon:infrastructure:resource_management:index'
|
|
success_message = _("Rack created.")
|
|
failure_message = _("Unable to create rack.")
|
|
|
|
# FIXME active tabs coflict
|
|
# When on page with tabs, the workflow with more steps is used,
|
|
# there is a conflict of active tabs and it always shows the
|
|
# first tab after an action. So I explicitly specify to what
|
|
# tab it should redirect after action, until the coflict will
|
|
# be fixed in Horizon.
|
|
def get_index_url(self):
|
|
"""This url is used both as success and failure url"""
|
|
return "%s?tab=resource_management_tabs__racks_tab" %\
|
|
urlresolvers.reverse('horizon:infrastructure:resource_management:'
|
|
'index')
|
|
|
|
def get_success_url(self):
|
|
return self.get_index_url()
|
|
|
|
def get_failure_url(self):
|
|
return self.get_index_url()
|
|
|
|
def create_or_update_node(self, node_data):
|
|
"""Creates (if id=='') or updates (otherwise) a node."""
|
|
if node_data['id'] not in ('', None):
|
|
node_id = unicode(node_data['id'])
|
|
# TODO(rdopieralski) there is currently no way to update
|
|
# a baremetal node
|
|
#
|
|
# tuskar.BaremetalNode.update(
|
|
# self.request,
|
|
# node_id=node_id,
|
|
# service_host=node_data['service_host'],
|
|
# cpus=node_data['cpus'],
|
|
# memory_mb=node_data['memory_mb'],
|
|
# local_gb=node_data['local_gb'],
|
|
# prov_mac_address=node_data['mac_address'],
|
|
# pm_address=node_data['pm_address'],
|
|
# pm_user=node_data['pm_user'],
|
|
# pm_password=node_data['pm_password'],
|
|
# terminal_port=node_data['terminal_port'])
|
|
return node_id
|
|
else:
|
|
node = tuskar.BaremetalNode.create(
|
|
self.request,
|
|
service_host=node_data['service_host'],
|
|
cpus=node_data['cpus'],
|
|
memory_mb=node_data['memory_mb'],
|
|
local_gb=node_data['local_gb'],
|
|
prov_mac_address=node_data['mac_address'],
|
|
pm_address=node_data['pm_address'],
|
|
pm_user=node_data['pm_user'],
|
|
pm_password=node_data['pm_password'],
|
|
terminal_port=node_data['terminal_port'])
|
|
return node.id
|
|
|
|
def handle(self, request, data):
|
|
# First, create and/or update nodes
|
|
node_ids = []
|
|
for node_data in data['nodes']:
|
|
try:
|
|
node_id = self.create_or_update_node(node_data)
|
|
except Exception:
|
|
exceptions.handle(self.request, _("Unable to update node."))
|
|
return False
|
|
else:
|
|
# Rack.create takes a list of dicts with BaremetalNode ids
|
|
node_ids.append({'id': node_id})
|
|
try:
|
|
# Then, register the Rack, including the nodes
|
|
tuskar.Rack.create(
|
|
request, name=data['name'],
|
|
resource_class_id=data['resource_class_id'],
|
|
location=data['location'], subnet=data['subnet'],
|
|
nodes=node_ids)
|
|
|
|
return True
|
|
except requests.ConnectionError:
|
|
messages.error(request,
|
|
_("Unable to connect to Nova Baremetal. Please "
|
|
"check your configuration."))
|
|
return False
|
|
except Exception:
|
|
exceptions.handle(request, _("Unable to create rack."))
|
|
return False
|
|
|
|
|
|
class EditRack(CreateRack):
|
|
default_steps = (EditRackInfo, EditNodes)
|
|
slug = "edit_rack"
|
|
name = _("Edit Rack")
|
|
success_url = 'horizon:infrastructure:resource_management:index'
|
|
success_message = _("Rack updated.")
|
|
failure_message = _("Unable to update rack.")
|
|
|
|
def handle(self, request, data):
|
|
node_ids = [{'id': self.create_or_update_node(node_data)}
|
|
for node_data in data['nodes']]
|
|
try:
|
|
rack_id = self.context['rack_id']
|
|
data['nodes'] = node_ids
|
|
tuskar.Rack.update(request, rack_id, data)
|
|
return True
|
|
except Exception:
|
|
exceptions.handle(request, _("Unable to update rack."))
|
|
return False
|
|
|
|
|
|
class DetailEditRack(EditRack):
|
|
success_url = 'horizon:infrastructure:resource_management:racks:detail'
|
|
|
|
def get_success_url(self):
|
|
rack_id = self.context['rack_id']
|
|
return urlresolvers.reverse(self.success_url, args=(rack_id,))
|
|
|
|
def get_failure_url(self):
|
|
rack_id = self.context['rack_id']
|
|
return urlresolvers.reverse(self.success_url, args=(rack_id,))
|