Remove kernel/image from flavor and put it on node
Starting with Juno, setting the kernel and image id on a flavor is deprecated. This moves that setting from the flavor, and puts it on the ironic node. Change-Id: I1f6072213d01adbc95e9d70d4faf95b376b5a566
This commit is contained in:
parent
10ec89b16e
commit
a09d5e54b9
@ -100,7 +100,9 @@ class IronicNode(base.APIResourceWrapper):
|
||||
def create(cls, request, ipmi_address=None, cpu_arch=None, cpus=None,
|
||||
memory_mb=None, local_gb=None, mac_addresses=[],
|
||||
ipmi_username=None, ipmi_password=None, ssh_address=None,
|
||||
ssh_username=None, ssh_key_contents=None, driver=None):
|
||||
ssh_username=None, ssh_key_contents=None,
|
||||
deployment_kernel=None, deployment_ramdisk=None,
|
||||
driver=None):
|
||||
"""Create a Node in Ironic."""
|
||||
if driver == 'pxe_ssh':
|
||||
driver_info = {
|
||||
@ -115,6 +117,10 @@ class IronicNode(base.APIResourceWrapper):
|
||||
'ipmi_username': ipmi_username,
|
||||
'ipmi_password': ipmi_password
|
||||
}
|
||||
driver_info.update(
|
||||
pxe_deploy_kernel=deployment_kernel,
|
||||
pxe_deploy_ramdisk=deployment_ramdisk
|
||||
)
|
||||
|
||||
properties = {}
|
||||
if cpus:
|
||||
@ -342,6 +348,7 @@ class BareMetalNode(base.APIResourceWrapper):
|
||||
def create(cls, request, ipmi_address, cpu_arch, cpus, memory_mb,
|
||||
local_gb, mac_addresses, ipmi_username=None, ipmi_password=None,
|
||||
ssh_address=None, ssh_username=None, ssh_key_contents=None,
|
||||
deployment_kernel=None, deployment_ramdisk=None,
|
||||
driver=None):
|
||||
"""Create a Nova BareMetalNode."""
|
||||
node = baremetalclient(request).create(
|
||||
@ -567,12 +574,15 @@ class Node(base.APIResourceWrapper):
|
||||
def create(cls, request, ipmi_address=None, cpu_arch=None, cpus=None,
|
||||
memory_mb=None, local_gb=None, mac_addresses=[],
|
||||
ipmi_username=None, ipmi_password=None, ssh_address=None,
|
||||
ssh_username=None, ssh_key_contents=None, driver=None):
|
||||
ssh_username=None, ssh_key_contents=None,
|
||||
deployment_kernel=None, deployment_ramdisk=None, driver=None):
|
||||
return cls(NodeClient(request).node_class.create(
|
||||
request, ipmi_address, cpu_arch, cpus, memory_mb, local_gb,
|
||||
mac_addresses, ipmi_username=ipmi_username,
|
||||
ipmi_password=ipmi_password, ssh_address=ssh_address,
|
||||
ssh_username=ssh_username, ssh_key_contents=ssh_key_contents,
|
||||
deployment_kernel=deployment_kernel,
|
||||
deployment_ramdisk=deployment_ramdisk,
|
||||
driver=driver))
|
||||
|
||||
@classmethod
|
||||
|
@ -16,41 +16,11 @@ from django.utils.translation import ugettext_lazy as _
|
||||
import horizon.exceptions
|
||||
import horizon.messages
|
||||
import horizon.tables
|
||||
from openstack_dashboard.api import glance
|
||||
from openstack_dashboard.dashboards.admin.flavors import (
|
||||
tables as flavor_tables)
|
||||
|
||||
from tuskar_ui import api
|
||||
from tuskar_ui.infrastructure.flavors import utils
|
||||
import tuskar_ui.utils.utils
|
||||
|
||||
DEFAULT_KERNEL_IMAGE_NAME = 'discovery-kernel'
|
||||
DEFAULT_RAMDISK_IMAGE_NAME = 'discovery-ramdisk'
|
||||
|
||||
|
||||
def _guess_default_image_ids(request):
|
||||
try:
|
||||
images = glance.image_list_detailed(request)[0]
|
||||
except Exception:
|
||||
horizon.exceptions.handle(request,
|
||||
_('Unable to retrieve images list.'))
|
||||
kernel_images = []
|
||||
ramdisk_images = []
|
||||
else:
|
||||
has_type = tuskar_ui.utils.utils.check_image_type
|
||||
kernel_images = [
|
||||
image for image in images
|
||||
if has_type(image, 'discovery kernel')
|
||||
and image.name == DEFAULT_KERNEL_IMAGE_NAME
|
||||
]
|
||||
ramdisk_images = [
|
||||
image for image in images
|
||||
if has_type(image, 'discovery ramdisk')
|
||||
and image.name == DEFAULT_RAMDISK_IMAGE_NAME
|
||||
]
|
||||
if not kernel_images or not ramdisk_images:
|
||||
raise ValueError("No default images")
|
||||
return kernel_images[0].id, ramdisk_images[0].id
|
||||
|
||||
|
||||
class CreateFlavor(flavor_tables.CreateFlavor):
|
||||
@ -65,33 +35,20 @@ class CreateSuggestedFlavor(horizon.tables.Action):
|
||||
method = 'POST'
|
||||
icon = 'plus'
|
||||
|
||||
def create_flavor(self, request, node_id, images):
|
||||
def create_flavor(self, request, node_id):
|
||||
node = api.node.Node.get(request, node_id)
|
||||
suggestion = utils.FlavorSuggestion.from_node(node)
|
||||
return suggestion.create_flavor(request, *images)
|
||||
return suggestion.create_flavor(request)
|
||||
|
||||
def handle(self, data_table, request, node_ids):
|
||||
try:
|
||||
images = _guess_default_image_ids(request)
|
||||
except ValueError:
|
||||
horizon.messages.error(request, _(
|
||||
"No default images available. "
|
||||
"Create images called \"{0}\" and \"{1}\" or "
|
||||
"use the \"{2}\" action to choose different image names."
|
||||
).format(
|
||||
DEFAULT_KERNEL_IMAGE_NAME,
|
||||
DEFAULT_RAMDISK_IMAGE_NAME,
|
||||
EditAndCreateSuggestedFlavor.verbose_name,
|
||||
))
|
||||
else:
|
||||
for node_id in node_ids:
|
||||
try:
|
||||
self.create_flavor(request, node_id, images)
|
||||
except Exception:
|
||||
horizon.exceptions.handle(
|
||||
request,
|
||||
_(u"Unable to create flavor for node %r") % node_id,
|
||||
)
|
||||
for node_id in node_ids:
|
||||
try:
|
||||
self.create_flavor(request, node_id)
|
||||
except Exception:
|
||||
horizon.exceptions.handle(
|
||||
request,
|
||||
_(u"Unable to create flavor for node %r") % node_id,
|
||||
)
|
||||
return django.shortcuts.redirect(request.get_full_path())
|
||||
|
||||
|
||||
|
@ -21,15 +21,6 @@
|
||||
<dd>{{ flavor.disk_bytes|filesizeformat|default:"—" }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4>{% trans "Deploy Images" %}</h4>
|
||||
<dl class="dl-horizontal dl-horizontal-left">
|
||||
<dt>{% trans "Kernel" %}</dt>
|
||||
<dd>{{ kernel_image.name|default:"—" }}</dd>
|
||||
<dt>{% trans "Ramdisk" %}</dt>
|
||||
<dd>{{ ramdisk_image.name|default:"—" }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
|
@ -43,19 +43,14 @@ DETAILS_VIEW = 'horizon:infrastructure:flavors:details'
|
||||
def _prepare_create():
|
||||
flavor = TEST_DATA.novaclient_flavors.first()
|
||||
all_flavors = TEST_DATA.novaclient_flavors.list()
|
||||
images = TEST_DATA.glanceclient_images.list()
|
||||
data = {'name': 'foobar',
|
||||
'vcpus': 3,
|
||||
'memory_mb': 1024,
|
||||
'disk_gb': 40,
|
||||
'arch': 'amd64',
|
||||
'kernel_image_id': images[5].id,
|
||||
'ramdisk_image_id': images[4].id}
|
||||
'arch': 'amd64'}
|
||||
with contextlib.nested(
|
||||
patch('tuskar_ui.api.flavor.Flavor.create',
|
||||
return_value=flavor),
|
||||
patch('openstack_dashboard.api.glance.image_list_detailed',
|
||||
return_value=(TEST_DATA.glanceclient_images.list(), False)),
|
||||
# Inherited code calls this directly
|
||||
patch('openstack_dashboard.api.nova.flavor_list',
|
||||
return_value=all_flavors),
|
||||
@ -108,26 +103,10 @@ class FlavorsTest(test.BaseAdminViewTests):
|
||||
self.assertMessageCount(response=res, error=2, warning=0)
|
||||
|
||||
def test_create_get(self):
|
||||
with patch('openstack_dashboard.api.glance.image_list_detailed',
|
||||
return_value=([], False)) as mock:
|
||||
res = self.client.get(CREATE_URL)
|
||||
self.assertEqual(mock.call_count, 2)
|
||||
res = self.client.get(CREATE_URL)
|
||||
self.assertTemplateUsed(res, 'infrastructure/flavors/create.html')
|
||||
|
||||
def test_create_get_recoverable_failure(self):
|
||||
with patch('openstack_dashboard.api.glance.image_list_detailed',
|
||||
side_effect=_raise_nova_client_exception):
|
||||
res = self.client.get(CREATE_URL)
|
||||
self.assertEqual(
|
||||
[(m.message, m.tags) for m in res.context['messages']],
|
||||
[
|
||||
(u'Unable to retrieve images list.', u'error'),
|
||||
],
|
||||
)
|
||||
self.assertMessageCount(response=res, error=1, warning=0)
|
||||
|
||||
def test_create_post_ok(self):
|
||||
images = TEST_DATA.glanceclient_images.list()
|
||||
with _prepare_create() as (create_mock, data):
|
||||
res = self.client.post(CREATE_URL, data)
|
||||
self.assertNoFormErrors(res)
|
||||
@ -135,8 +114,7 @@ class FlavorsTest(test.BaseAdminViewTests):
|
||||
request = create_mock.call_args_list[0][0][0]
|
||||
self.assertListEqual(create_mock.call_args_list, [
|
||||
call(request, name=u'foobar', memory=1024, vcpus=3, disk=40,
|
||||
cpu_arch='amd64', kernel_image_id=images[5].id,
|
||||
ramdisk_image_id=images[4].id)
|
||||
cpu_arch='amd64')
|
||||
])
|
||||
|
||||
def test_create_post_name_exists(self):
|
||||
@ -158,8 +136,6 @@ class FlavorsTest(test.BaseAdminViewTests):
|
||||
return_value=[]),
|
||||
patch('tuskar_ui.api.tuskar.Plan.list',
|
||||
return_value=[]),
|
||||
patch('openstack_dashboard.api.glance.image_list_detailed',
|
||||
return_value=([], False)),
|
||||
patch('openstack_dashboard.api.nova.flavor_list',
|
||||
return_value=TEST_DATA.novaclient_flavors.list())
|
||||
):
|
||||
@ -187,8 +163,6 @@ class FlavorsTest(test.BaseAdminViewTests):
|
||||
return_value=[]),
|
||||
patch('tuskar_ui.api.tuskar.Plan.list',
|
||||
return_value=[]),
|
||||
patch('openstack_dashboard.api.glance.image_list_detailed',
|
||||
return_value=([], False)),
|
||||
patch('openstack_dashboard.api.nova.flavor_list',
|
||||
return_value=TEST_DATA.novaclient_flavors.list()),
|
||||
patch('tuskar_ui.api.node.Node.list',
|
||||
@ -201,24 +175,20 @@ class FlavorsTest(test.BaseAdminViewTests):
|
||||
|
||||
def test_details_no_overcloud(self):
|
||||
flavor = api.flavor.Flavor(TEST_DATA.novaclient_flavors.first())
|
||||
images = TEST_DATA.glanceclient_images.list()[:2]
|
||||
plan = api.tuskar.Plan(TEST_DATA.tuskarclient_plans.first())
|
||||
roles = [api.tuskar.Role(role)
|
||||
for role in self.tuskarclient_roles.list()]
|
||||
|
||||
with contextlib.nested(
|
||||
patch('openstack_dashboard.api.glance.image_get',
|
||||
side_effect=images),
|
||||
patch('tuskar_ui.api.flavor.Flavor.get',
|
||||
return_value=flavor),
|
||||
patch('tuskar_ui.api.tuskar.Plan.get_the_plan',
|
||||
return_value=plan),
|
||||
patch('tuskar_ui.api.tuskar.Role.list', return_value=roles),
|
||||
patch('tuskar_ui.api.tuskar.Role.flavor', return_value=flavor),
|
||||
) as (image_mock, get_mock, plan_mock, roles_mock, role_flavor_mock):
|
||||
) as (get_mock, plan_mock, roles_mock, role_flavor_mock):
|
||||
res = self.client.get(urlresolvers.reverse(DETAILS_VIEW,
|
||||
args=(flavor.id,)))
|
||||
self.assertEqual(image_mock.call_count, 1)
|
||||
self.assertEqual(get_mock.call_count, 1)
|
||||
self.assertEqual(plan_mock.call_count, 2)
|
||||
self.assertEqual(roles_mock.call_count, 1)
|
||||
@ -227,15 +197,12 @@ class FlavorsTest(test.BaseAdminViewTests):
|
||||
|
||||
def test_details(self):
|
||||
flavor = api.flavor.Flavor(TEST_DATA.novaclient_flavors.first())
|
||||
images = TEST_DATA.glanceclient_images.list()[:2]
|
||||
plan = api.tuskar.Plan(TEST_DATA.tuskarclient_plans.first())
|
||||
roles = [api.tuskar.Role(role)
|
||||
for role in self.tuskarclient_roles.list()]
|
||||
stack = api.heat.Stack(TEST_DATA.heatclient_stacks.first())
|
||||
|
||||
with contextlib.nested(
|
||||
patch('openstack_dashboard.api.glance.image_get',
|
||||
side_effect=images),
|
||||
patch('tuskar_ui.api.flavor.Flavor.get',
|
||||
return_value=flavor),
|
||||
patch('tuskar_ui.api.tuskar.Plan.get_the_plan',
|
||||
@ -247,11 +214,10 @@ class FlavorsTest(test.BaseAdminViewTests):
|
||||
# __name__ is required for horizon.tables
|
||||
patch('tuskar_ui.api.heat.Stack.resources_count',
|
||||
return_value=42, __name__='')
|
||||
) as (image_mock, flavor_mock, plan_mock, roles_mock, role_flavor_mock,
|
||||
) as (flavor_mock, plan_mock, roles_mock, role_flavor_mock,
|
||||
stack_mock, count_mock):
|
||||
res = self.client.get(urlresolvers.reverse(DETAILS_VIEW,
|
||||
args=(flavor.id,)))
|
||||
self.assertEqual(image_mock.call_count, 1)
|
||||
self.assertEqual(flavor_mock.call_count, 1)
|
||||
self.assertEqual(plan_mock.call_count, 2)
|
||||
self.assertEqual(roles_mock.call_count, 1)
|
||||
|
@ -110,8 +110,7 @@ class FlavorSuggestion(object):
|
||||
)
|
||||
)
|
||||
|
||||
def create_flavor(self, request,
|
||||
kernel_image_id=None, ramdisk_image_id=None):
|
||||
def create_flavor(self, request):
|
||||
return api.flavor.Flavor.create(
|
||||
request,
|
||||
name=self.name,
|
||||
@ -119,6 +118,4 @@ class FlavorSuggestion(object):
|
||||
vcpus=self.vcpus,
|
||||
disk=self.disk,
|
||||
cpu_arch=self.cpu_arch,
|
||||
kernel_image_id=kernel_image_id,
|
||||
ramdisk_image_id=ramdisk_image_id,
|
||||
)
|
||||
|
@ -92,14 +92,6 @@ class DetailView(horizon.tables.DataTableView):
|
||||
kwargs.get('flavor_id'),
|
||||
_error_redirect=self.error_redirect
|
||||
)
|
||||
context['kernel_image'] = api.node.image_get(
|
||||
self.request,
|
||||
context['flavor'].kernel_image_id
|
||||
)
|
||||
context['ramdisk_image'] = api.node.image_get(
|
||||
self.request,
|
||||
context['flavor'].ramdisk_image_id
|
||||
)
|
||||
return context
|
||||
|
||||
def get_data(self):
|
||||
|
@ -16,47 +16,19 @@ from django.forms import fields
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from horizon import exceptions
|
||||
from horizon import workflows
|
||||
from openstack_dashboard.api import glance
|
||||
from openstack_dashboard.dashboards.admin.flavors import (
|
||||
workflows as flavor_workflows)
|
||||
|
||||
from tuskar_ui import api
|
||||
from tuskar_ui.utils import utils
|
||||
|
||||
|
||||
class CreateFlavorAction(flavor_workflows.CreateFlavorInfoAction):
|
||||
arch = fields.ChoiceField(choices=(('i386', 'i386'), ('amd64', 'amd64'),
|
||||
('x86_64', 'x86_64')),
|
||||
label=_("Architecture"))
|
||||
kernel_image_id = fields.ChoiceField(choices=(),
|
||||
label=_("Deploy Kernel Image"))
|
||||
ramdisk_image_id = fields.ChoiceField(choices=(),
|
||||
label=_("Deploy Ramdisk Image"))
|
||||
|
||||
def __init__(self, *args, **kwrds):
|
||||
super(CreateFlavorAction, self).__init__(*args, **kwrds)
|
||||
try:
|
||||
kernel_images = glance.image_list_detailed(
|
||||
self.request,
|
||||
)[0]
|
||||
kernel_images = [image for image in kernel_images
|
||||
if utils.check_image_type(image,
|
||||
'discovery kernel')]
|
||||
ramdisk_images = glance.image_list_detailed(
|
||||
self.request,
|
||||
)[0]
|
||||
ramdisk_images = [image for image in ramdisk_images
|
||||
if utils.check_image_type(image,
|
||||
'discovery ramdisk')]
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve images list.'))
|
||||
kernel_images = []
|
||||
ramdisk_images = []
|
||||
self.fields['kernel_image_id'].choices = [(img.id, img.name)
|
||||
for img in kernel_images]
|
||||
self.fields['ramdisk_image_id'].choices = [(img.id, img.name)
|
||||
for img in ramdisk_images]
|
||||
# Delete what is not applicable to hardware
|
||||
del self.fields['eph_gb']
|
||||
del self.fields['swap_mb']
|
||||
@ -79,9 +51,7 @@ class CreateFlavorStep(workflows.Step):
|
||||
"vcpus",
|
||||
"memory_mb",
|
||||
"disk_gb",
|
||||
"arch",
|
||||
"kernel_image_id",
|
||||
"ramdisk_image_id")
|
||||
"arch")
|
||||
|
||||
|
||||
class CreateFlavor(flavor_workflows.CreateFlavor):
|
||||
@ -101,9 +71,7 @@ class CreateFlavor(flavor_workflows.CreateFlavor):
|
||||
memory=data['memory_mb'],
|
||||
vcpus=data['vcpus'],
|
||||
disk=data['disk_gb'],
|
||||
cpu_arch=data['arch'],
|
||||
kernel_image_id=data['kernel_image_id'],
|
||||
ramdisk_image_id=data['ramdisk_image_id']
|
||||
cpu_arch=data['arch']
|
||||
)
|
||||
except Exception:
|
||||
exceptions.handle(request, _("Unable to create flavor"))
|
||||
|
@ -23,6 +23,9 @@ from tuskar_ui import api
|
||||
import tuskar_ui.forms
|
||||
|
||||
|
||||
DEFAULT_KERNEL_IMAGE_NAME = 'bm-deploy-kernel'
|
||||
DEFAULT_RAMDISK_IMAGE_NAME = 'bm-deploy-ramdisk'
|
||||
|
||||
CPU_ARCH_CHOICES = [
|
||||
('', _("unspecified")),
|
||||
('amd64', _("amd64")),
|
||||
@ -37,7 +40,10 @@ DRIVER_CHOICES = [
|
||||
|
||||
def get_driver_info_dict(data):
|
||||
driver = data['driver']
|
||||
driver_dict = {'driver': driver}
|
||||
driver_dict = {'driver': driver,
|
||||
'deployment_kernel': data['deployment_kernel'],
|
||||
'deployment_ramdisk': data['deployment_ramdisk'],
|
||||
}
|
||||
if driver == 'pxe_ipmitool':
|
||||
driver_dict.update(
|
||||
ipmi_address=data['ipmi_address'],
|
||||
@ -203,6 +209,18 @@ class NodeForm(django.forms.Form):
|
||||
widget=tuskar_ui.forms.NumberInput(
|
||||
attrs={'placeholder': _('unspecified')}),
|
||||
)
|
||||
deployment_kernel = django.forms.ChoiceField(
|
||||
label=_("Kernel"),
|
||||
required=False,
|
||||
choices=[],
|
||||
widget=django.forms.Select(),
|
||||
)
|
||||
deployment_ramdisk = django.forms.ChoiceField(
|
||||
label=_("Ramdisk"),
|
||||
required=False,
|
||||
choices=[],
|
||||
widget=django.forms.Select(),
|
||||
)
|
||||
|
||||
def get_name(self):
|
||||
try:
|
||||
@ -244,6 +262,19 @@ class NodeForm(django.forms.Form):
|
||||
|
||||
|
||||
class BaseNodeFormset(tuskar_ui.forms.SelfHandlingFormset):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.kernel_images = kwargs.pop('kernel_images')
|
||||
self.ramdisk_images = kwargs.pop('ramdisk_images')
|
||||
super(BaseNodeFormset, self).__init__(*args, **kwargs)
|
||||
|
||||
def add_fields(self, form, index):
|
||||
deployment_kernel_choices = [(kernel.id, kernel.name)
|
||||
for kernel in self.kernel_images]
|
||||
deployment_ramdisk_choices = [(ramdisk.id, ramdisk.name)
|
||||
for ramdisk in self.ramdisk_images]
|
||||
form.fields['deployment_kernel'].choices = deployment_kernel_choices
|
||||
form.fields['deployment_ramdisk'].choices = deployment_ramdisk_choices
|
||||
|
||||
def clean(self):
|
||||
all_macs = api.node.Node.get_all_mac_addresses(self.request)
|
||||
bad_macs = set()
|
||||
|
@ -307,6 +307,15 @@ class DetailOverviewTab(tabs.Tab):
|
||||
context['role'] = resource.role
|
||||
context['stack'] = resource.stack
|
||||
|
||||
context['kernel_image'] = api.node.image_get(
|
||||
request,
|
||||
node.driver_info['pxe_deploy_kernel']
|
||||
)
|
||||
context['ramdisk_image'] = api.node.image_get(
|
||||
request,
|
||||
node.driver_info['pxe_deploy_ramdisk']
|
||||
)
|
||||
|
||||
if node.instance_uuid:
|
||||
if api_base.is_service_enabled(self.request, 'metering'):
|
||||
# Meter configuration in the following format:
|
||||
|
@ -19,6 +19,7 @@ from ceilometerclient.v2 import client as ceilometer_client
|
||||
from django.core import urlresolvers
|
||||
from horizon import exceptions as horizon_exceptions
|
||||
from mock import patch, call, ANY # noqa
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
from openstack_dashboard.test.test_data import utils
|
||||
|
||||
from tuskar_ui import api
|
||||
@ -40,6 +41,10 @@ heat_data.data(TEST_DATA)
|
||||
tuskar_data.data(TEST_DATA)
|
||||
|
||||
|
||||
def _raise_nova_client_exception(*args, **kwargs):
|
||||
raise nova_exceptions.ClientException("Boom!")
|
||||
|
||||
|
||||
class NodesTests(test.BaseAdminViewTests):
|
||||
@handle_errors("Error!", [])
|
||||
def _raise_tuskar_exception(self, request, *args, **kwargs):
|
||||
@ -127,13 +132,17 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
self._test_index_tab_list_exception('maintenance')
|
||||
|
||||
def test_register_get(self):
|
||||
res = self.client.get(REGISTER_URL)
|
||||
with patch('openstack_dashboard.api.glance.image_list_detailed',
|
||||
return_value=([], False)) as mock:
|
||||
res = self.client.get(REGISTER_URL)
|
||||
self.assertEqual(mock.call_count, 2)
|
||||
self.assertTemplateUsed(
|
||||
res, 'infrastructure/nodes/register.html')
|
||||
|
||||
def test_register_post(self):
|
||||
node = TEST_DATA.ironicclient_nodes.first
|
||||
nodes = self._all_mocked_nodes()
|
||||
images = self.glanceclient_images.list()
|
||||
data = {
|
||||
'register_nodes-TOTAL_FORMS': 2,
|
||||
'register_nodes-INITIAL_FORMS': 1,
|
||||
@ -148,6 +157,8 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
'register_nodes-0-cpus': '1',
|
||||
'register_nodes-0-memory_mb': '2',
|
||||
'register_nodes-0-local_gb': '3',
|
||||
'register_nodes-0-deployment_kernel': images[6].id,
|
||||
'register_nodes-0-deployment_ramdisk': images[7].id,
|
||||
|
||||
'register_nodes-1-driver': 'pxe_ipmitool',
|
||||
'register_nodes-1-ipmi_address': '127.0.0.2',
|
||||
@ -156,12 +167,18 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
'register_nodes-1-cpus': '4',
|
||||
'register_nodes-1-memory_mb': '5',
|
||||
'register_nodes-1-local_gb': '6',
|
||||
'register_nodes-1-deployment_kernel': images[6].id,
|
||||
'register_nodes-1-deployment_ramdisk': images[7].id,
|
||||
}
|
||||
with patch('tuskar_ui.api.node.Node', **{
|
||||
'spec_set': ['create', 'get_all_mac_addresses'],
|
||||
'create.return_value': node,
|
||||
'get_all_mac_addresses.return_value': set(nodes),
|
||||
}) as Node:
|
||||
with contextlib.nested(
|
||||
patch('tuskar_ui.api.node.Node', **{
|
||||
'spec_set': ['create', 'get_all_mac_addresses'],
|
||||
'create.return_value': node,
|
||||
'get_all_mac_addresses.return_value': set(nodes),
|
||||
}),
|
||||
patch('openstack_dashboard.api.glance.image_list_detailed',
|
||||
return_value=[images, False, False]),
|
||||
) as (Node, mock_glance_images):
|
||||
res = self.client.post(REGISTER_URL, data)
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
@ -177,6 +194,8 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
ipmi_username=u'username',
|
||||
ipmi_password=u'password',
|
||||
driver='pxe_ipmitool',
|
||||
deployment_kernel=images[6].id,
|
||||
deployment_ramdisk=images[7].id,
|
||||
),
|
||||
call(
|
||||
ANY,
|
||||
@ -189,11 +208,14 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
ipmi_username=None,
|
||||
ipmi_password=None,
|
||||
driver='pxe_ipmitool',
|
||||
deployment_kernel=images[6].id,
|
||||
deployment_ramdisk=images[7].id,
|
||||
),
|
||||
])
|
||||
|
||||
def test_register_post_exception(self):
|
||||
nodes = self._all_mocked_nodes()
|
||||
images = self.glanceclient_images.list()
|
||||
data = {
|
||||
'register_nodes-TOTAL_FORMS': 2,
|
||||
'register_nodes-INITIAL_FORMS': 1,
|
||||
@ -208,6 +230,8 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
'register_nodes-0-cpus': '1',
|
||||
'register_nodes-0-memory_mb': '2',
|
||||
'register_nodes-0-local_gb': '3',
|
||||
'register_nodes-0-deployment_kernel': images[6].id,
|
||||
'register_nodes-0-deployment_ramdisk': images[7].id,
|
||||
|
||||
'register_nodes-1-driver': 'pxe_ipmitool',
|
||||
'register_nodes-1-ipmi_address': '127.0.0.2',
|
||||
@ -216,12 +240,18 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
'register_nodes-1-cpus': '4',
|
||||
'register_nodes-1-memory_mb': '5',
|
||||
'register_nodes-1-local_gb': '6',
|
||||
'register_nodes-1-deployment_kernel': images[6].id,
|
||||
'register_nodes-1-deployment_ramdisk': images[7].id,
|
||||
}
|
||||
with patch('tuskar_ui.api.node.Node', **{
|
||||
'spec_set': ['create', 'get_all_mac_addresses'],
|
||||
'create.side_effect': self.exceptions.tuskar,
|
||||
'get_all_mac_addresses.return_value': set(nodes),
|
||||
}) as Node:
|
||||
with contextlib.nested(
|
||||
patch('tuskar_ui.api.node.Node', **{
|
||||
'spec_set': ['create', 'get_all_mac_addresses'],
|
||||
'create.side_effect': self.exceptions.tuskar,
|
||||
'get_all_mac_addresses.return_value': set(nodes),
|
||||
}),
|
||||
patch('openstack_dashboard.api.glance.image_list_detailed',
|
||||
return_value=[images, False, False]),
|
||||
) as (Node, mock_glance_images):
|
||||
res = self.client.post(REGISTER_URL, data)
|
||||
self.assertEqual(res.status_code, 200)
|
||||
self.assertListEqual(Node.create.call_args_list, [
|
||||
@ -236,6 +266,8 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
ipmi_username=u'username',
|
||||
ipmi_password=u'password',
|
||||
driver='pxe_ipmitool',
|
||||
deployment_kernel=images[6].id,
|
||||
deployment_ramdisk=images[7].id,
|
||||
),
|
||||
call(
|
||||
ANY,
|
||||
@ -248,6 +280,8 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
ipmi_username=None,
|
||||
ipmi_password=None,
|
||||
driver='pxe_ipmitool',
|
||||
deployment_kernel=images[6].id,
|
||||
deployment_ramdisk=images[7].id,
|
||||
),
|
||||
])
|
||||
self.assertTemplateUsed(
|
||||
@ -255,6 +289,7 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
|
||||
def test_node_detail(self):
|
||||
node = api.node.Node(self.ironicclient_nodes.list()[0])
|
||||
image = self.glanceclient_images.first()
|
||||
|
||||
with contextlib.nested(
|
||||
patch('tuskar_ui.api.node.Node', **{
|
||||
@ -266,7 +301,9 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
'get_by_node.side_effect': lambda *args, **kwargs: {}[None],
|
||||
# Raises LookupError
|
||||
}),
|
||||
) as (mock_node, mock_heat):
|
||||
patch('openstack_dashboard.api.glance.image_get',
|
||||
return_value=image),
|
||||
) as (mock_node, mock_heat, mock_glance):
|
||||
res = self.client.get(
|
||||
urlresolvers.reverse(DETAIL_VIEW, args=(node.uuid,))
|
||||
)
|
||||
@ -421,6 +458,8 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
'ipmi_address': '127.0.0.1',
|
||||
'ipmi_username': 'root',
|
||||
'ipmi_password': 'P@55W0rd',
|
||||
'deployment_kernel': '7',
|
||||
'deployment_ramdisk': '8',
|
||||
}
|
||||
ret = forms.get_driver_info_dict(data)
|
||||
self.assertEqual(ret, {
|
||||
@ -428,12 +467,16 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
'ipmi_address': '127.0.0.1',
|
||||
'ipmi_username': 'root',
|
||||
'ipmi_password': 'P@55W0rd',
|
||||
'deployment_kernel': '7',
|
||||
'deployment_ramdisk': '8',
|
||||
})
|
||||
data = {
|
||||
'driver': 'pxe_ssh',
|
||||
'ssh_address': '127.0.0.1',
|
||||
'ssh_username': 'root',
|
||||
'ssh_key_contents': 'P@55W0rd',
|
||||
'deployment_kernel': '7',
|
||||
'deployment_ramdisk': '8',
|
||||
}
|
||||
ret = forms.get_driver_info_dict(data)
|
||||
self.assertEqual(ret, {
|
||||
@ -441,6 +484,8 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
'ssh_address': '127.0.0.1',
|
||||
'ssh_username': 'root',
|
||||
'ssh_key_contents': 'P@55W0rd',
|
||||
'deployment_kernel': '7',
|
||||
'deployment_ramdisk': '8',
|
||||
})
|
||||
|
||||
def test_create_node(self):
|
||||
@ -454,6 +499,8 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
'ipmi_username': 'username',
|
||||
'ipmi_password': 'password',
|
||||
'driver': 'pxe_ipmitool',
|
||||
'deployment_kernel': '7',
|
||||
'deployment_ramdisk': '8',
|
||||
}
|
||||
with patch('tuskar_ui.api.node.Node', **{
|
||||
'spec_set': ['create', 'set_maintenance', 'discover'],
|
||||
@ -472,5 +519,7 @@ class NodesTests(test.BaseAdminViewTests):
|
||||
ipmi_username=u'username',
|
||||
ipmi_password=u'password',
|
||||
driver='pxe_ipmitool',
|
||||
deployment_kernel='7',
|
||||
deployment_ramdisk='8',
|
||||
),
|
||||
])
|
||||
|
@ -19,9 +19,11 @@ import django.forms
|
||||
import django.http
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import base
|
||||
from horizon import exceptions
|
||||
from horizon import forms as horizon_forms
|
||||
from horizon import tabs as horizon_tabs
|
||||
from horizon.utils import memoized
|
||||
from openstack_dashboard.api import glance
|
||||
|
||||
from tuskar_ui import api
|
||||
from tuskar_ui.infrastructure.nodes import forms
|
||||
@ -29,6 +31,36 @@ from tuskar_ui.infrastructure.nodes import tables
|
||||
from tuskar_ui.infrastructure.nodes import tabs
|
||||
import tuskar_ui.infrastructure.views as infrastructure_views
|
||||
from tuskar_ui.utils import metering as metering_utils
|
||||
from tuskar_ui.utils import utils
|
||||
|
||||
|
||||
def get_kernel_images(request):
|
||||
try:
|
||||
kernel_images = glance.image_list_detailed(
|
||||
request,
|
||||
)[0]
|
||||
kernel_images = [image for image in kernel_images
|
||||
if utils.check_image_type(image, 'deploy kernel')]
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve kernel image list.'))
|
||||
kernel_images = []
|
||||
return kernel_images
|
||||
|
||||
|
||||
def get_ramdisk_images(request):
|
||||
try:
|
||||
ramdisk_images = glance.image_list_detailed(
|
||||
request,
|
||||
)[0]
|
||||
ramdisk_images = [image for image in ramdisk_images
|
||||
if utils.check_image_type(
|
||||
image, 'deploy ramdisk')]
|
||||
except Exception:
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve ramdisk image list.'))
|
||||
ramdisk_images = []
|
||||
return ramdisk_images
|
||||
|
||||
|
||||
class IndexView(infrastructure_views.ItemCountMixin,
|
||||
@ -78,7 +110,9 @@ class RegisterView(horizon_forms.ModalFormView):
|
||||
formset = forms.RegisterNodeFormset(
|
||||
self.request.POST,
|
||||
prefix=self.form_prefix,
|
||||
request=self.request
|
||||
request=self.request,
|
||||
kernel_images=get_kernel_images(self.request),
|
||||
ramdisk_images=get_ramdisk_images(self.request)
|
||||
)
|
||||
if formset.is_valid():
|
||||
initial += formset.cleaned_data
|
||||
@ -86,7 +120,9 @@ class RegisterView(horizon_forms.ModalFormView):
|
||||
None,
|
||||
initial=initial,
|
||||
prefix=self.form_prefix,
|
||||
request=self.request
|
||||
request=self.request,
|
||||
kernel_images=get_kernel_images(self.request),
|
||||
ramdisk_images=get_ramdisk_images(self.request)
|
||||
)
|
||||
formset.extra = 0
|
||||
return formset
|
||||
@ -94,7 +130,9 @@ class RegisterView(horizon_forms.ModalFormView):
|
||||
self.request.POST or None,
|
||||
initial=initial,
|
||||
prefix=self.form_prefix,
|
||||
request=self.request
|
||||
request=self.request,
|
||||
kernel_images=get_kernel_images(self.request),
|
||||
ramdisk_images=get_ramdisk_images(self.request)
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@ -109,6 +147,7 @@ class DetailView(horizon_tabs.TabView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
|
||||
node = self.get_data()
|
||||
|
||||
if node.maintenance:
|
||||
|
@ -85,6 +85,14 @@
|
||||
<dd>{{ node.instance_uuid|default:"—" }}</dd>
|
||||
</dl>
|
||||
|
||||
<h3>{% trans "Deployment Images" %}</h3>
|
||||
<dl class="dl-horizontal dl-horizontal-left">
|
||||
<dt>{% trans "Kernel" %}</dt>
|
||||
<dd>{{ kernel_image.name|default:"—" }}</dd>
|
||||
<dt>{% trans "Ramdisk" %}</dt>
|
||||
<dd>{{ ramdisk_image.name|default:"—" }}</dd>
|
||||
</dl>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-6 col-xs-12">
|
||||
<h3>{% trans "Performance & Metrics" %}</h3>
|
||||
|
@ -27,6 +27,11 @@
|
||||
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.memory_mb extra_text=_('MB') %}
|
||||
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.local_gb extra_text=_('GB') %}
|
||||
</div>
|
||||
<div class="param-section">
|
||||
<h5>{% trans "Deployment Images" %}</h5>
|
||||
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.deployment_kernel %}
|
||||
{% include 'infrastructure/nodes/_nodes_formset_field.html' with field=form.deployment_ramdisk %}
|
||||
</div>
|
||||
</fieldset></div>
|
||||
</div>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends 'infrastructure/base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Register Nodes" %}{% endblock %}
|
||||
|
||||
|
@ -254,5 +254,23 @@ def data(TEST):
|
||||
'properties': {
|
||||
'type': 'discovery kernel'
|
||||
}})
|
||||
image_7 = images.Image(
|
||||
images.ImageManager(None),
|
||||
{'id': '7',
|
||||
'name': 'Baremetal Deployment Kernel',
|
||||
'is_public': True,
|
||||
'protected': False,
|
||||
'properties': {
|
||||
'type': 'deploy kernel'
|
||||
}})
|
||||
image_8 = images.Image(
|
||||
images.ImageManager(None),
|
||||
{'id': '8',
|
||||
'name': 'Baremetal Deployment Ramdisk',
|
||||
'is_public': True,
|
||||
'protected': False,
|
||||
'properties': {
|
||||
'type': 'deploy ramdisk'
|
||||
}})
|
||||
TEST.glanceclient_images.add(image_1, image_2, image_3, image_4,
|
||||
image_5, image_6)
|
||||
image_5, image_6, image_7, image_8)
|
||||
|
@ -124,7 +124,9 @@ def data(TEST):
|
||||
'ipmi_address': '1.1.1.1',
|
||||
'ipmi_username': 'admin',
|
||||
'ipmi_password': 'password',
|
||||
'ip_address': '1.2.2.2'
|
||||
'ip_address': '1.2.2.2',
|
||||
'pxe_deploy_kernel': 'deploy-kernel-uuid',
|
||||
'pxe_deploy_ramdisk': 'deploy-ramdisk-uuid',
|
||||
},
|
||||
'properties': {
|
||||
'cpus': '8',
|
||||
@ -149,7 +151,9 @@ def data(TEST):
|
||||
'ipmi_address': '2.2.2.2',
|
||||
'ipmi_username': 'admin',
|
||||
'ipmi_password': 'password',
|
||||
'ip_address': '1.2.2.3'
|
||||
'ip_address': '1.2.2.3',
|
||||
'pxe_deploy_kernel': 'deploy-kernel-uuid',
|
||||
'pxe_deploy_ramdisk': 'deploy-ramdisk-uuid',
|
||||
},
|
||||
'properties': {
|
||||
'cpus': '16',
|
||||
@ -174,7 +178,9 @@ def data(TEST):
|
||||
'ipmi_address': '3.3.3.3',
|
||||
'ipmi_username': 'admin',
|
||||
'ipmi_password': 'password',
|
||||
'ip_address': '1.2.2.4'
|
||||
'ip_address': '1.2.2.4',
|
||||
'pxe_deploy_kernel': 'deploy-kernel-uuid',
|
||||
'pxe_deploy_ramdisk': 'deploy-ramdisk-uuid',
|
||||
},
|
||||
'properties': {
|
||||
'cpus': '32',
|
||||
@ -199,7 +205,9 @@ def data(TEST):
|
||||
'ipmi_address': '4.4.4.4',
|
||||
'ipmi_username': 'admin',
|
||||
'ipmi_password': 'password',
|
||||
'ip_address': '1.2.2.5'
|
||||
'ip_address': '1.2.2.5',
|
||||
'pxe_deploy_kernel': 'deploy-kernel-uuid',
|
||||
'pxe_deploy_ramdisk': 'deploy-ramdisk-uuid',
|
||||
},
|
||||
'properties': {
|
||||
'cpus': '8',
|
||||
@ -224,7 +232,9 @@ def data(TEST):
|
||||
'ipmi_address': '5.5.5.5',
|
||||
'ipmi_username': 'admin',
|
||||
'ipmi_password': 'password',
|
||||
'ip_address': '1.2.2.6'
|
||||
'ip_address': '1.2.2.6',
|
||||
'pxe_deploy_kernel': 'deploy-kernel-uuid',
|
||||
'pxe_deploy_ramdisk': 'deploy-ramdisk-uuid',
|
||||
},
|
||||
'properties': {
|
||||
'cpus': '8',
|
||||
@ -249,7 +259,9 @@ def data(TEST):
|
||||
'ipmi_address': '5.5.5.5',
|
||||
'ipmi_username': 'admin',
|
||||
'ipmi_password': 'password',
|
||||
'ip_address': '1.2.2.6'
|
||||
'ip_address': '1.2.2.6',
|
||||
'pxe_deploy_kernel': 'deploy-kernel-uuid',
|
||||
'pxe_deploy_ramdisk': 'deploy-ramdisk-uuid',
|
||||
},
|
||||
'properties': {
|
||||
'cpus': '8',
|
||||
@ -274,7 +286,9 @@ def data(TEST):
|
||||
'ipmi_address': '7.7.7.7',
|
||||
'ipmi_username': 'admin',
|
||||
'ipmi_password': 'password',
|
||||
'ip_address': '1.2.2.7'
|
||||
'ip_address': '1.2.2.7',
|
||||
'pxe_deploy_kernel': 'deploy-kernel-uuid',
|
||||
'pxe_deploy_ramdisk': 'deploy-ramdisk-uuid',
|
||||
},
|
||||
'properties': {
|
||||
'cpus': '8',
|
||||
@ -299,7 +313,9 @@ def data(TEST):
|
||||
'ipmi_address': '8.8.8.8',
|
||||
'ipmi_username': 'admin',
|
||||
'ipmi_password': 'password',
|
||||
'ip_address': '1.2.2.8'
|
||||
'ip_address': '1.2.2.8',
|
||||
'pxe_deploy_kernel': 'deploy-kernel-uuid',
|
||||
'pxe_deploy_ramdisk': 'deploy-ramdisk-uuid',
|
||||
},
|
||||
'properties': {
|
||||
'cpus': '8',
|
||||
@ -324,7 +340,9 @@ def data(TEST):
|
||||
'ipmi_address': '9.9.9.9',
|
||||
'ipmi_username': 'admin',
|
||||
'ipmi_password': 'password',
|
||||
'ip_address': '1.2.2.9'
|
||||
'ip_address': '1.2.2.9',
|
||||
'pxe_deploy_kernel': 'deploy-kernel-uuid',
|
||||
'pxe_deploy_ramdisk': 'deploy-ramdisk-uuid',
|
||||
},
|
||||
'properties': {
|
||||
'cpus': '16',
|
||||
|
Loading…
x
Reference in New Issue
Block a user