817 lines
31 KiB
Python
Executable File
817 lines
31 KiB
Python
Executable File
#
|
|
# Copyright (c) 2013-2014, 2017 Wind River Systems, Inc.
|
|
#
|
|
# The right to copy, distribute, modify, or otherwise make use
|
|
# of this software may be licensed only pursuant to the terms
|
|
# of an applicable Wind River license agreement.
|
|
#
|
|
|
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
import logging
|
|
|
|
from cgtsclient import exc
|
|
from django.core.urlresolvers import reverse # noqa
|
|
from django import shortcuts
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
from horizon import exceptions
|
|
from horizon import forms
|
|
from horizon import messages
|
|
from openstack_dashboard import api
|
|
from openstack_dashboard.api import sysinv
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
class AddDiskProfile(forms.SelfHandlingForm):
|
|
host_id = forms.CharField(widget=forms.widgets.HiddenInput)
|
|
profilename = forms.CharField(label=_("Storage Profile Name"),
|
|
required=True)
|
|
|
|
failure_url = 'horizon:admin:inventory:detail'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(AddDiskProfile, self).__init__(*args, **kwargs)
|
|
|
|
def clean(self):
|
|
cleaned_data = super(AddDiskProfile, self).clean()
|
|
# host_id = cleaned_data.get('host_id')
|
|
return cleaned_data
|
|
|
|
def handle(self, request, data):
|
|
diskProfileName = data['profilename']
|
|
try:
|
|
diskProfile = api.sysinv.host_diskprofile_create(request, **data)
|
|
|
|
msg = _('Storage Profile "%s" was successfully created.') \
|
|
% diskProfileName
|
|
LOG.debug(msg)
|
|
|
|
messages.success(request, msg)
|
|
|
|
return diskProfile
|
|
except Exception as e:
|
|
msg = _('Failed to create storage profile "%s".') % diskProfileName
|
|
LOG.info(msg)
|
|
LOG.error(e)
|
|
|
|
messages.error(request, e)
|
|
|
|
redirect = reverse(self.failure_url,
|
|
args=[data['host_id']])
|
|
return shortcuts.redirect(redirect)
|
|
|
|
|
|
class EditStorageVolume(forms.SelfHandlingForm):
|
|
id = forms.CharField(widget=forms.widgets.HiddenInput)
|
|
|
|
journal_locations = forms.ChoiceField(label=_("Journal"),
|
|
required=False,
|
|
widget=forms.Select(attrs={
|
|
'data-slug':
|
|
'journal_locations'}),
|
|
help_text=_("Assign disk to journal "
|
|
"storage volume."))
|
|
|
|
journal_size_mib = forms.CharField(label=_("Journal Size MiB"),
|
|
required=False,
|
|
initial=sysinv.JOURNAL_DEFAULT_SIZE,
|
|
widget=forms.TextInput(attrs={
|
|
'data-slug': 'journal_size_mib'}),
|
|
help_text=_("Journal's size for the "
|
|
"current OSD."))
|
|
|
|
failure_url = 'horizon:admin:inventory:detail'
|
|
|
|
def __init__(self, request, *args, **kwargs):
|
|
super(EditStorageVolume, self).__init__(request, *args, **kwargs)
|
|
|
|
stor = api.sysinv.host_stor_get(
|
|
self.request, kwargs['initial']['uuid'])
|
|
|
|
initial_journal_location = kwargs['initial']['journal_location']
|
|
host_uuid = kwargs['initial']['host_uuid']
|
|
|
|
# Populate available journal choices. If no journal is available,
|
|
# then the journal is collocated.
|
|
avail_journal_list = api.sysinv.host_stor_get_by_function(
|
|
self.request,
|
|
host_uuid,
|
|
'journal')
|
|
|
|
journal_tuple_list = []
|
|
|
|
if stor.uuid == initial_journal_location:
|
|
journal_tuple_list.append((stor.uuid, "Collocated with OSD"))
|
|
else:
|
|
journal_tuple_list.append((initial_journal_location,
|
|
"%s " % initial_journal_location))
|
|
|
|
if avail_journal_list:
|
|
for j in avail_journal_list:
|
|
if j.uuid != initial_journal_location:
|
|
journal_tuple_list.append((j.uuid, "%s " % j.uuid))
|
|
|
|
if stor.uuid != initial_journal_location:
|
|
journal_tuple_list.append((stor.uuid, "Collocated with OSD"))
|
|
|
|
self.fields['journal_locations'].choices = journal_tuple_list
|
|
|
|
def handle(self, request, data):
|
|
stor_id = data['id']
|
|
|
|
try:
|
|
# Obtain journal information.
|
|
journal = data['journal_locations'][:]
|
|
|
|
if journal:
|
|
data['journal_location'] = journal
|
|
else:
|
|
data['journal_location'] = None
|
|
data['journal_size_mib'] = sysinv.JOURNAL_DEFAULT_SIZE
|
|
|
|
del data['journal_locations']
|
|
del data['id']
|
|
|
|
# The REST API takes care of updating the stor journal information.
|
|
stor = api.sysinv.host_stor_update(request, stor_id, **data)
|
|
|
|
msg = _('Storage volume was successfully updated.')
|
|
LOG.debug(msg)
|
|
messages.success(request, msg)
|
|
|
|
return stor
|
|
except exc.ClientException as ce:
|
|
msg = _('Failed to update storage volume.')
|
|
LOG.info(msg)
|
|
LOG.error(ce)
|
|
|
|
# Allow REST API error message to appear on UI.
|
|
messages.error(request, ce)
|
|
|
|
# Redirect to host details pg.
|
|
redirect = reverse(self.failure_url, args=[stor_id])
|
|
return shortcuts.redirect(redirect)
|
|
except Exception as e:
|
|
msg = _('Failed to update storage volume.')
|
|
LOG.info(msg)
|
|
LOG.error(e)
|
|
|
|
# if not a rest API error, throw default
|
|
redirect = reverse(self.failure_url, args=[stor_id])
|
|
return exceptions.handle(request, message=e, redirect=redirect)
|
|
|
|
|
|
class AddStorageVolume(forms.SelfHandlingForm):
|
|
# Only allowed to choose 'osd'
|
|
FUNCTION_CHOICES = (
|
|
('osd', _("osd")),
|
|
('journal', _("journal")),
|
|
# ('monitor', _("monitor")),
|
|
)
|
|
|
|
host_id = forms.CharField(label=_("host_id"),
|
|
initial='host_id',
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
ihost_uuid = forms.CharField(label=_("ihost_uuid"),
|
|
initial='ihost_uuid',
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
idisk_uuid = forms.CharField(label=_("idisk_uuid"),
|
|
initial='idisk_uuid',
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
hostname = forms.CharField(label=_("Hostname"),
|
|
initial='hostname',
|
|
widget=forms.TextInput(attrs={
|
|
'readonly': 'readonly'}))
|
|
|
|
function = forms.ChoiceField(label=_("Function"),
|
|
required=False,
|
|
choices=FUNCTION_CHOICES,
|
|
widget=forms.Select(attrs={
|
|
'class': 'switchable',
|
|
'data-slug': 'function'}))
|
|
|
|
disks = forms.ChoiceField(label=_("Disks"),
|
|
required=True,
|
|
widget=forms.Select(attrs={
|
|
'class': 'switchable',
|
|
'data-slug': 'disk'}),
|
|
help_text=_("Assign disk to storage volume."))
|
|
|
|
journal_locations = forms.ChoiceField(label=_("Journal"),
|
|
required=False,
|
|
widget=forms.Select(attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'function',
|
|
'data-function-osd': _(
|
|
"Journal")}),
|
|
help_text=_("Assign disk to journal "
|
|
"storage volume."))
|
|
|
|
journal_size_mib = forms.CharField(label=_("Journal Size MiB"),
|
|
required=False,
|
|
initial=sysinv.JOURNAL_DEFAULT_SIZE,
|
|
widget=forms.TextInput(attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'function',
|
|
'data-function-osd':
|
|
_("Journal Size MiB")}),
|
|
help_text=_("Journal's size for the"
|
|
"current OSD."))
|
|
|
|
failure_url = 'horizon:admin:inventory:detail'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(AddStorageVolume, self).__init__(*args, **kwargs)
|
|
|
|
# Populate available disk choices
|
|
this_stor_uuid = 0
|
|
host_uuid = kwargs['initial']['ihost_uuid']
|
|
|
|
ihost = api.sysinv.host_get(self.request, host_uuid)
|
|
ceph_caching = ((ihost.capabilities.get('pers_subtype') ==
|
|
sysinv.PERSONALITY_SUBTYPE_CEPH_CACHING))
|
|
|
|
avail_disk_list = api.sysinv.host_disk_list(self.request, host_uuid)
|
|
disk_tuple_list = []
|
|
for d in avail_disk_list:
|
|
if d.istor_uuid and d.istor_uuid != this_stor_uuid:
|
|
continue
|
|
is_rootfs_device = \
|
|
(('stor_function' in d.capabilities)
|
|
and (d.capabilities['stor_function'] == 'rootfs'))
|
|
if is_rootfs_device:
|
|
continue
|
|
disk_model = d.get_model_num()
|
|
if disk_model is not None and "floppy" in disk_model.lower():
|
|
continue
|
|
if (ceph_caching and d.device_type != 'SSD' and
|
|
d.device_type != 'NVME'):
|
|
continue
|
|
disk_tuple_list.append(
|
|
(d.uuid, "%s (path: %s size:%s model:%s type: %s)" % (
|
|
d.device_node,
|
|
d.device_path,
|
|
str(d.size_mib),
|
|
disk_model,
|
|
d.device_type)))
|
|
|
|
# Populate available journal choices. If no journal is available,
|
|
# then the journal is collocated.
|
|
if ceph_caching:
|
|
avail_journal_list = []
|
|
else:
|
|
avail_journal_list = api.sysinv.host_stor_get_by_function(
|
|
self.request,
|
|
host_uuid,
|
|
'journal')
|
|
|
|
journal_tuple_list = []
|
|
if avail_journal_list:
|
|
for j in avail_journal_list:
|
|
journal_tuple_list.append((j.uuid, "%s " % j.uuid))
|
|
else:
|
|
journal_tuple_list.append((None, "Collocated with OSD"))
|
|
self.fields['journal_size_mib'].widget.attrs['disabled'] = \
|
|
'disabled'
|
|
|
|
if ceph_caching:
|
|
self.fields['function'].choices = (
|
|
AddStorageVolume.FUNCTION_CHOICES[:1])
|
|
|
|
self.fields['disks'].choices = disk_tuple_list
|
|
self.fields['journal_locations'].choices = journal_tuple_list
|
|
|
|
def clean(self):
|
|
cleaned_data = super(AddStorageVolume, self).clean()
|
|
# host_id = cleaned_data.get('host_id')
|
|
# ihost_uuid = cleaned_data.get('ihost_uuid')
|
|
|
|
# disks = cleaned_data.get('disks')
|
|
|
|
return cleaned_data
|
|
|
|
def handle(self, request, data):
|
|
host_id = data['host_id']
|
|
# host_uuid = data['ihost_uuid']
|
|
disks = data['disks'][:] # copy
|
|
|
|
# GUI only allows one disk to be picked
|
|
data['idisk_uuid'] = disks
|
|
|
|
# Obtain journal information.
|
|
journal = data['journal_locations'][:]
|
|
|
|
if journal:
|
|
data['journal_location'] = journal
|
|
else:
|
|
data['journal_location'] = None
|
|
data['journal_size_mib'] = sysinv.JOURNAL_DEFAULT_SIZE
|
|
|
|
try:
|
|
del data['host_id']
|
|
del data['disks']
|
|
del data['hostname']
|
|
del data['journal_locations']
|
|
|
|
# The REST API takes care of creating the stor
|
|
# and updating disk.foristorid
|
|
stor = api.sysinv.host_stor_create(request, **data)
|
|
|
|
msg = _('Storage volume was successfully created.')
|
|
LOG.debug(msg)
|
|
messages.success(request, msg)
|
|
|
|
return stor
|
|
except exc.ClientException as ce:
|
|
msg = _('Failed to create storage volume.')
|
|
LOG.info(msg)
|
|
LOG.error(ce)
|
|
|
|
# Allow REST API error message to appear on UI
|
|
messages.error(request, ce)
|
|
|
|
# Redirect to host details pg
|
|
redirect = reverse(self.failure_url, args=[host_id])
|
|
return shortcuts.redirect(redirect)
|
|
except Exception as e:
|
|
msg = _('Failed to create storage volume.')
|
|
LOG.info(msg)
|
|
LOG.error(e)
|
|
|
|
# if not a rest API error, throw default
|
|
redirect = reverse(self.failure_url, args=[host_id])
|
|
return exceptions.handle(request, message=e, redirect=redirect)
|
|
|
|
|
|
class AddLocalVolumeGroup(forms.SelfHandlingForm):
|
|
|
|
host_id = forms.CharField(label=_("host_id"),
|
|
initial='host_id',
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
ihost_uuid = forms.CharField(label=_("ihost_uuid"),
|
|
initial='ihost_uuid',
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
hostname = forms.CharField(label=_("Hostname"),
|
|
initial='hostname',
|
|
widget=forms.TextInput(attrs={
|
|
'readonly': 'readonly'}))
|
|
|
|
lvm_vg_name = forms.ChoiceField(label=_("Local Volume Group Name"),
|
|
required=True,
|
|
widget=forms.Select(attrs={
|
|
'class': 'switchable',
|
|
'data-slug': 'lvm_vg_name'}))
|
|
|
|
failure_url = 'horizon:admin:inventory:detail'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(AddLocalVolumeGroup, self).__init__(*args, **kwargs)
|
|
|
|
# Populate available volume group choices
|
|
host_uuid = kwargs['initial']['ihost_uuid']
|
|
host_id = kwargs['initial']['host_id']
|
|
|
|
host = api.sysinv.host_get(self.request, host_id)
|
|
subfunctions = host.subfunctions
|
|
|
|
# LVGs that are considered as "present" in the system are those
|
|
# in an adding or provisioned state.
|
|
ilvg_list = api.sysinv.host_lvg_list(self.request, host_uuid)
|
|
current_lvg_states = [api.sysinv.LVG_ADD, api.sysinv.LVG_PROV]
|
|
current_lvgs = [lvg.lvm_vg_name for lvg in ilvg_list
|
|
if lvg.vg_state in current_lvg_states]
|
|
|
|
compatible_lvgs = []
|
|
if host.personality.lower().startswith(
|
|
api.sysinv.PERSONALITY_CONTROLLER):
|
|
compatible_lvgs += [api.sysinv.LVG_CINDER_VOLUMES]
|
|
|
|
if api.sysinv.SUBFUNCTIONS_COMPUTE in subfunctions:
|
|
compatible_lvgs += [api.sysinv.LVG_NOVA_LOCAL]
|
|
|
|
allowed_lvgs = set(compatible_lvgs) - set(current_lvgs)
|
|
|
|
ilvg_tuple_list = []
|
|
for lvg in allowed_lvgs:
|
|
ilvg_tuple_list.append((lvg, lvg))
|
|
|
|
self.fields['lvm_vg_name'].choices = ilvg_tuple_list
|
|
|
|
def clean(self):
|
|
cleaned_data = super(AddLocalVolumeGroup, self).clean()
|
|
return cleaned_data
|
|
|
|
def handle(self, request, data):
|
|
host_id = data['host_id']
|
|
|
|
try:
|
|
del data['host_id']
|
|
del data['hostname']
|
|
|
|
# The REST API takes care of creating the stor
|
|
# and updating disk.foristorid
|
|
lvg = api.sysinv.host_lvg_create(request, **data)
|
|
|
|
msg = _('Local volume group was successfully created.')
|
|
LOG.debug(msg)
|
|
messages.success(request, msg)
|
|
|
|
return lvg
|
|
except exc.ClientException as ce:
|
|
msg = _('Failed to create local volume group.')
|
|
LOG.info(msg)
|
|
LOG.error(ce)
|
|
|
|
# Allow REST API error message to appear on UI
|
|
w_msg = str(ce)
|
|
if ('Warning' in w_msg):
|
|
LOG.info(ce)
|
|
messages.warning(request, w_msg.split(':', 1)[-1])
|
|
else:
|
|
LOG.error(ce)
|
|
messages.error(request, w_msg)
|
|
|
|
# Redirect to host details pg
|
|
redirect = reverse(self.failure_url, args=[host_id])
|
|
return shortcuts.redirect(redirect)
|
|
except Exception as e:
|
|
msg = _('Failed to create local volume group.')
|
|
LOG.info(msg)
|
|
LOG.error(e)
|
|
|
|
# if not a rest API error, throw default
|
|
redirect = reverse(self.failure_url, args=[host_id])
|
|
return exceptions.handle(request, message=e, redirect=redirect)
|
|
|
|
|
|
class AddPhysicalVolume(forms.SelfHandlingForm):
|
|
|
|
PV_TYPE_CHOICES = (
|
|
('disk', _("Disk")),
|
|
('partition', _("Partition")),
|
|
)
|
|
|
|
host_id = forms.CharField(label=_("host_id"),
|
|
initial='host_id',
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
ihost_uuid = forms.CharField(label=_("ihost_uuid"),
|
|
initial='ihost_uuid',
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
disk_or_part_uuid = forms.CharField(label=_("disk_or_part_uuid"),
|
|
initial='disk_or_part_uuid',
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
hostname = forms.CharField(label=_("Hostname"),
|
|
initial='hostname',
|
|
widget=forms.TextInput(attrs={
|
|
'readonly': 'readonly'}))
|
|
|
|
lvg = forms.ChoiceField(label=_("Local Volume Group"),
|
|
required=True,
|
|
widget=forms.Select(attrs={
|
|
'class': 'switchable',
|
|
'data-slug': 'lvg'}),
|
|
help_text=_("Associate this physical volume to a "
|
|
"volume group "))
|
|
|
|
pv_type = forms.ChoiceField(label=_("PV Type"),
|
|
required=False,
|
|
choices=PV_TYPE_CHOICES,
|
|
widget=forms.Select(attrs={
|
|
'class': 'switchable',
|
|
'data-slug': 'pv_type',
|
|
'initial': 'Disk'}))
|
|
|
|
disks = forms.ChoiceField(label=_("Disks"),
|
|
required=False,
|
|
widget=forms.Select(attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'pv_type',
|
|
'data-pv_type-disk': _("Disks")}),
|
|
help_text=_("Assign disk to physical volume."))
|
|
|
|
partitions = forms.ChoiceField(label=_("Partitions"),
|
|
required=False,
|
|
widget=forms.Select(attrs={
|
|
'class': 'switched',
|
|
'data-switch-on': 'pv_type',
|
|
'data-pv_type-partition':
|
|
_("Partitions")}),
|
|
help_text=_("Assign partition to physical "
|
|
"volume."))
|
|
|
|
failure_url = 'horizon:admin:inventory:detail'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(AddPhysicalVolume, self).__init__(*args, **kwargs)
|
|
|
|
# Populate available partition, disk, and volume group choices
|
|
host_uuid = kwargs['initial']['ihost_uuid']
|
|
host_id = kwargs['initial']['host_id']
|
|
|
|
host = api.sysinv.host_get(self.request, host_id)
|
|
subfunctions = host.subfunctions
|
|
|
|
compatible_lvgs = []
|
|
if host.personality.lower().startswith(
|
|
api.sysinv.PERSONALITY_CONTROLLER):
|
|
compatible_lvgs += [api.sysinv.LVG_CGTS_VG,
|
|
api.sysinv.LVG_CINDER_VOLUMES]
|
|
|
|
if api.sysinv.SUBFUNCTIONS_COMPUTE in subfunctions:
|
|
compatible_lvgs += [api.sysinv.LVG_NOVA_LOCAL]
|
|
|
|
avail_disk_list = api.sysinv.host_disk_list(self.request, host_uuid)
|
|
ilvg_list = api.sysinv.host_lvg_list(self.request, host_uuid)
|
|
partitions = api.sysinv.host_disk_partition_list(self.request,
|
|
host_uuid)
|
|
ipv_list = api.sysinv.host_pv_list(self.request, host_uuid)
|
|
disk_tuple_list = []
|
|
partitions_tuple_list = []
|
|
ilvg_tuple_list = []
|
|
|
|
for lvg in ilvg_list:
|
|
if (lvg.lvm_vg_name in compatible_lvgs and
|
|
lvg.vg_state in [api.sysinv.LVG_ADD, api.sysinv.LVG_PROV]):
|
|
ilvg_tuple_list.append((lvg.uuid, lvg.lvm_vg_name))
|
|
|
|
for disk in avail_disk_list:
|
|
capabilities = disk.capabilities
|
|
|
|
if capabilities.get('stor_function') == 'rootfs':
|
|
continue
|
|
# TODO(rchurch): re-factor
|
|
elif capabilities.get('device_function') == 'cinder_device':
|
|
continue
|
|
else:
|
|
break
|
|
|
|
for d in avail_disk_list:
|
|
disk_cap = d.capabilities
|
|
# TODO(rchurch): re-factor
|
|
is_cinder_device = \
|
|
(('device_function' in disk_cap)
|
|
and (disk_cap['device_function'] == 'cinder_device'))
|
|
|
|
is_rootfs_device = \
|
|
(('stor_function' in disk_cap)
|
|
and (disk_cap['stor_function'] == 'rootfs'))
|
|
|
|
disk_model = d.get_model_num()
|
|
# TODO(rchurch): re-factor
|
|
if not d.ipv_uuid and is_cinder_device:
|
|
continue
|
|
|
|
if is_rootfs_device or d.ipv_uuid:
|
|
continue
|
|
|
|
if disk_model is not None and "floppy" in disk_model.lower():
|
|
continue
|
|
|
|
disk_tuple_list.append(
|
|
(d.uuid, "%s (path:%s size:%s model:%s)" % (
|
|
d.device_node,
|
|
d.device_path,
|
|
str(d.size_mib),
|
|
disk_model)))
|
|
|
|
for p in partitions:
|
|
if p.type_guid != api.sysinv.USER_PARTITION_PHYS_VOL:
|
|
continue
|
|
if p.ipv_uuid:
|
|
continue
|
|
if p.status == api.sysinv.PARTITION_IN_USE_STATUS:
|
|
# If partition is in use, but the PV it is attached to
|
|
# is in a "removing" state, we should allow the partition
|
|
# to be listed as a possible option.
|
|
for pv in ipv_list:
|
|
if (pv.disk_or_part_device_path == p.device_path and
|
|
pv.pv_state == api.sysinv.PV_DEL):
|
|
break
|
|
else:
|
|
continue
|
|
|
|
partitions_tuple_list.append(
|
|
(p.uuid, "%s (size:%s)" % (
|
|
p.device_path,
|
|
str(p.size_mib))))
|
|
|
|
self.fields['disks'].choices = disk_tuple_list
|
|
self.fields['lvg'].choices = ilvg_tuple_list
|
|
self.fields['partitions'].choices = partitions_tuple_list
|
|
|
|
def clean(self):
|
|
cleaned_data = super(AddPhysicalVolume, self).clean()
|
|
return cleaned_data
|
|
|
|
def handle(self, request, data):
|
|
host_id = data['host_id']
|
|
|
|
lvgs = data['lvg'][:]
|
|
|
|
# GUI only allows one disk to be picked
|
|
if data['pv_type'] == 'disk':
|
|
data['disk_or_part_uuid'] = data['disks'][:]
|
|
else:
|
|
data['disk_or_part_uuid'] = data['partitions'][:]
|
|
|
|
data['ilvg_uuid'] = lvgs
|
|
|
|
try:
|
|
del data['host_id']
|
|
del data['disks']
|
|
del data['hostname']
|
|
del data['lvg']
|
|
del data['partitions']
|
|
|
|
stor = api.sysinv.host_pv_create(request, **data)
|
|
|
|
msg = _('Physical volume was successfully created.')
|
|
messages.success(request, msg)
|
|
return stor
|
|
except exc.ClientException as ce:
|
|
msg = _('Failed to create physical volume.')
|
|
|
|
# Allow REST API error message to appear on UI
|
|
w_msg = str(ce)
|
|
if ('Warning:' in w_msg):
|
|
LOG.info(ce)
|
|
messages.warning(request, w_msg.split(':', 1)[-1])
|
|
else:
|
|
LOG.error(ce)
|
|
messages.error(request, w_msg)
|
|
|
|
# Redirect to host details pg
|
|
redirect = reverse(self.failure_url, args=[host_id])
|
|
return shortcuts.redirect(redirect)
|
|
except Exception as e:
|
|
msg = _('Failed to create physical volume.')
|
|
LOG.error(e)
|
|
|
|
# if not a rest API error, throw default
|
|
redirect = reverse(self.failure_url, args=[host_id])
|
|
return exceptions.handle(request, message=e, redirect=redirect)
|
|
|
|
|
|
class EditPartition(forms.SelfHandlingForm):
|
|
id = forms.CharField(widget=forms.widgets.HiddenInput)
|
|
|
|
size_mib = forms.CharField(label=_("Partition Size MiB"),
|
|
required=False,
|
|
initial='size_mib',
|
|
widget=forms.TextInput(attrs={
|
|
'data-slug': 'size_mib'}),
|
|
help_text=_(
|
|
"New partition size. Has to be "
|
|
"larger than current size."))
|
|
|
|
def __init__(self, request, *args, **kwargs):
|
|
super(EditPartition, self).__init__(request, *args, **kwargs)
|
|
|
|
def handle(self, request, data):
|
|
partition_id = data['id']
|
|
|
|
try:
|
|
del data['id']
|
|
# The REST API takes care of updating the partition information.
|
|
partition = api.sysinv.host_disk_partition_update(
|
|
request, partition_id, **data)
|
|
|
|
msg = _('Partition was successfully updated.')
|
|
LOG.debug(msg)
|
|
messages.success(request, msg)
|
|
|
|
return partition
|
|
except exc.ClientException as ce:
|
|
msg = _('Failed to update partition.')
|
|
LOG.info(msg)
|
|
LOG.error(ce)
|
|
|
|
# No redirect, return to previous storage tab view.
|
|
# The REST API error message will appear on UI as
|
|
# "horizon.exceptions.handle" will invoke "messages.error".
|
|
return exceptions.handle(request, message=ce)
|
|
except Exception as e:
|
|
msg = _('Failed to update partition.')
|
|
LOG.info(msg)
|
|
LOG.error(e)
|
|
|
|
# If not a rest API error, throw default.
|
|
# No redirect, return to previous storage tab view.
|
|
return exceptions.handle(request, message=e)
|
|
|
|
|
|
class CreatePartition(forms.SelfHandlingForm):
|
|
host_id = forms.CharField(label=_("host_id"),
|
|
initial='host_id',
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
ihost_uuid = forms.CharField(label=_("ihost_uuid"),
|
|
initial='ihost_uuid',
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
idisk_uuid = forms.CharField(label=_("idisk_uuid"),
|
|
initial='idisk_uuid',
|
|
widget=forms.widgets.HiddenInput)
|
|
|
|
hostname = forms.CharField(label=_("Hostname"),
|
|
initial='hostname',
|
|
widget=forms.TextInput(attrs={
|
|
'readonly': 'readonly'}))
|
|
|
|
disks = forms.ChoiceField(label=_("Disks"),
|
|
required=True,
|
|
widget=forms.Select(attrs={
|
|
'class': 'switchable',
|
|
'data-slug': 'disk'}),
|
|
help_text=_("Disk to create partition on."))
|
|
|
|
size_mib = forms.IntegerField(label=_("Partition Size MiB"),
|
|
required=True,
|
|
initial=1024,
|
|
widget=forms.TextInput(attrs={
|
|
'data-slug': 'size_mib'}),
|
|
help_text=_("Size in MiB for the new "
|
|
"partition."))
|
|
|
|
type_guid = forms.CharField(label=_("Partition Type"),
|
|
initial='LVM Physical Volume',
|
|
widget=forms.TextInput(attrs={
|
|
'readonly': 'readonly'}))
|
|
|
|
failure_url = 'horizon:admin:inventory:detail'
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(CreatePartition, self).__init__(*args, **kwargs)
|
|
|
|
# Populate disk choices.
|
|
host_uuid = kwargs['initial']['ihost_uuid']
|
|
avail_disk_list = api.sysinv.host_disk_list(self.request, host_uuid)
|
|
disk_tuple_list = []
|
|
for d in avail_disk_list:
|
|
disk_model = d.get_model_num()
|
|
if disk_model is not None and "floppy" in disk_model.lower():
|
|
continue
|
|
if d.available_mib == 0:
|
|
continue
|
|
disk_tuple_list.append(
|
|
(d.uuid, "%s (path: %s size:%s available_mib:%s type: %s)" % (
|
|
d.device_node,
|
|
d.device_path,
|
|
str(d.size_mib),
|
|
str(d.available_mib),
|
|
d.device_type)))
|
|
|
|
self.fields['disks'].choices = disk_tuple_list
|
|
|
|
def clean(self):
|
|
cleaned_data = super(CreatePartition, self).clean()
|
|
return cleaned_data
|
|
|
|
def handle(self, request, data):
|
|
host_id = data['host_id']
|
|
disks = data['disks'][:]
|
|
data['idisk_uuid'] = disks
|
|
|
|
try:
|
|
del data['host_id']
|
|
del data['disks']
|
|
del data['hostname']
|
|
data['type_guid'] = sysinv.USER_PARTITION_PHYS_VOL
|
|
# The REST API takes care of creating the partition.
|
|
partition = api.sysinv.host_disk_partition_create(request, **data)
|
|
|
|
msg = _('Partition was successfully created.')
|
|
LOG.debug(msg)
|
|
messages.success(request, msg)
|
|
|
|
return partition
|
|
except exc.ClientException as ce:
|
|
msg = _('Failed to create partition.')
|
|
LOG.info(msg)
|
|
LOG.error(ce)
|
|
|
|
# Allow REST API error message to appear on UI
|
|
messages.error(request, ce)
|
|
|
|
# Redirect to host details pg
|
|
redirect = reverse(self.failure_url, args=[host_id])
|
|
return shortcuts.redirect(redirect)
|
|
except Exception as e:
|
|
msg = _('Failed to create partition.')
|
|
LOG.info(msg)
|
|
LOG.error(e)
|
|
|
|
# If not a REST API error, throw default.
|
|
redirect = reverse(self.failure_url, args=[host_id])
|
|
return exceptions.handle(request, message=e, redirect=redirect)
|