Merge "Support a host create operation"

This commit is contained in:
Zuul 2017-11-02 09:38:28 +00:00 committed by Gerrit Code Review
commit 43309a8532
8 changed files with 210 additions and 21 deletions

View File

@ -118,6 +118,12 @@ def host_get(request, host_id):
return Host(host)
def host_create(request, name, **kwargs):
"""Create a host."""
host = blazarclient(request).host.create(name, **kwargs)
return Host(host)
def host_delete(request, host_id):
"""Delete a host."""
blazarclient(request).host.delete(host_id)

View File

@ -18,6 +18,14 @@ from horizon.templatetags import sizeformat
from blazar_dashboard import api
class CreateHosts(tables.LinkAction):
name = "create"
verbose_name = _("Create Hosts")
url = "horizon:admin:hosts:create"
classes = ("ajax-modal",)
icon = "plus"
class DeleteHost(tables.DeleteAction):
name = "delete"
data_type_singular = _("Host")
@ -57,5 +65,5 @@ class HostsTable(tables.DataTable):
class Meta(object):
name = "hosts"
verbose_name = _("Hosts")
table_actions = (DeleteHost,)
table_actions = (CreateHosts, DeleteHost,)
row_actions = (DeleteHost,)

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Create Hosts" %}{% endblock %}
{% block main %}
{% include 'horizon/common/_workflow.html' %}
{% endblock %}

View File

@ -13,8 +13,9 @@
from django.core.urlresolvers import reverse
from django import http
from mox3.mox import IsA
from openstack_dashboard import api
from blazar_dashboard import api
from blazar_dashboard import api as blazar_api
from blazar_dashboard.test import helpers as test
import logging
@ -24,13 +25,15 @@ INDEX_TEMPLATE = 'admin/hosts/index.html'
INDEX_URL = reverse('horizon:admin:hosts:index')
DETAIL_TEMPLATE = 'admin/hosts/detail.html'
DETAIL_URL_BASE = 'horizon:admin:hosts:detail'
CREATE_URL = reverse('horizon:admin:hosts:create')
CREATE_TEMPLATE = 'admin/hosts/create.html'
class HostsTests(test.BaseAdminViewTests):
@test.create_stubs({api.client: ('host_list',)})
@test.create_stubs({blazar_api.client: ('host_list',)})
def test_index(self):
hosts = self.hosts.list()
api.client.host_list(IsA(http.HttpRequest)).AndReturn(hosts)
blazar_api.client.host_list(IsA(http.HttpRequest)).AndReturn(hosts)
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
@ -39,9 +42,9 @@ class HostsTests(test.BaseAdminViewTests):
self.assertContains(res, 'compute-1')
self.assertContains(res, 'compute-2')
@test.create_stubs({api.client: ('host_list',)})
@test.create_stubs({blazar_api.client: ('host_list',)})
def test_index_no_hosts(self):
api.client.host_list(IsA(http.HttpRequest)).AndReturn(())
blazar_api.client.host_list(IsA(http.HttpRequest)).AndReturn(())
self.mox.ReplayAll()
res = self.client.get(INDEX_URL)
@ -49,9 +52,9 @@ class HostsTests(test.BaseAdminViewTests):
self.assertNoMessages(res)
self.assertContains(res, 'No items to display')
@test.create_stubs({api.client: ('host_list',)})
@test.create_stubs({blazar_api.client: ('host_list',)})
def test_index_error(self):
api.client.host_list(
blazar_api.client.host_list(
IsA(http.HttpRequest)
).AndRaise(self.exceptions.blazar)
self.mox.ReplayAll()
@ -60,11 +63,11 @@ class HostsTests(test.BaseAdminViewTests):
self.assertTemplateUsed(res, INDEX_TEMPLATE)
self.assertMessageCount(res, error=1)
@test.create_stubs({api.client: ('host_get',)})
@test.create_stubs({blazar_api.client: ('host_get',)})
def test_host_detail(self):
host = self.hosts.get(hypervisor_hostname='compute-1')
api.client.host_get(IsA(http.HttpRequest),
host['id']).AndReturn(host)
blazar_api.client.host_get(IsA(http.HttpRequest),
host['id']).AndReturn(host)
self.mox.ReplayAll()
res = self.client.get(reverse(DETAIL_URL_BASE, args=[host['id']]))
@ -72,10 +75,10 @@ class HostsTests(test.BaseAdminViewTests):
self.assertContains(res, 'compute-1')
self.assertContains(res, 'ex1')
@test.create_stubs({api.client: ('host_get',)})
@test.create_stubs({blazar_api.client: ('host_get',)})
def test_host_detail_error(self):
api.client.host_get(IsA(http.HttpRequest),
'invalid').AndRaise(self.exceptions.blazar)
blazar_api.client.host_get(IsA(http.HttpRequest),
'invalid').AndRaise(self.exceptions.blazar)
self.mox.ReplayAll()
res = self.client.get(reverse(DETAIL_URL_BASE, args=['invalid']))
@ -83,12 +86,35 @@ class HostsTests(test.BaseAdminViewTests):
self.assertMessageCount(error=1)
self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api.client: ('host_list', 'host_delete')})
@test.create_stubs({blazar_api.client: ('host_list', 'host_create',),
api.nova: ('host_list',)})
def test_create_hosts(self):
blazar_api.client.host_list(IsA(http.HttpRequest)
).AndReturn([])
api.nova.host_list(IsA(http.HttpRequest)
).AndReturn(self.novahosts.list())
host_names = [h.host_name for h in self.novahosts.list()]
for host_name in host_names:
blazar_api.client.host_create(
IsA(http.HttpRequest),
name=host_name,
).AndReturn([])
self.mox.ReplayAll()
form_data = {
'select_hosts_role_member': host_names
}
res = self.client.post(CREATE_URL, form_data)
self.assertNoFormErrors(res)
self.assertMessageCount(success=(len(host_names) + 1))
self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({blazar_api.client: ('host_list', 'host_delete')})
def test_delete_host(self):
hosts = self.hosts.list()
host = self.hosts.get(hypervisor_hostname='compute-1')
api.client.host_list(IsA(http.HttpRequest)).AndReturn(hosts)
api.client.host_delete(IsA(http.HttpRequest), host['id'])
blazar_api.client.host_list(IsA(http.HttpRequest)).AndReturn(hosts)
blazar_api.client.host_delete(IsA(http.HttpRequest), host['id'])
self.mox.ReplayAll()
action = 'hosts__delete__%s' % host['id']
@ -97,13 +123,14 @@ class HostsTests(test.BaseAdminViewTests):
self.assertMessageCount(success=1)
self.assertRedirectsNoFollow(res, INDEX_URL)
@test.create_stubs({api.client: ('host_list', 'host_delete')})
@test.create_stubs({blazar_api.client: ('host_list', 'host_delete')})
def test_delete_host_error(self):
hosts = self.hosts.list()
host = self.hosts.get(hypervisor_hostname='compute-1')
api.client.host_list(IsA(http.HttpRequest)).AndReturn(hosts)
api.client.host_delete(IsA(http.HttpRequest),
host['id']).AndRaise(self.exceptions.blazar)
blazar_api.client.host_list(IsA(http.HttpRequest)).AndReturn(hosts)
blazar_api.client.host_delete(
IsA(http.HttpRequest),
host['id']).AndRaise(self.exceptions.blazar)
self.mox.ReplayAll()
action = 'hosts__delete__%s' % host['id']

View File

@ -17,5 +17,6 @@ from blazar_dashboard.content.hosts import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^create/$', views.CreateView.as_view(), name='create'),
url(r'^(?P<host_id>[^/]+)/$', views.DetailView.as_view(), name='detail')
]

View File

@ -14,10 +14,12 @@ from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tables
from horizon import tabs
from horizon import workflows
from blazar_dashboard import api
from blazar_dashboard.content.hosts import tables as project_tables
from blazar_dashboard.content.hosts import tabs as project_tabs
from blazar_dashboard.content.hosts import workflows as project_workflows
class IndexView(tables.DataTableView):
@ -37,3 +39,9 @@ class IndexView(tables.DataTableView):
class DetailView(tabs.TabView):
tab_group_class = project_tabs.HostDetailTabs
template_name = 'admin/hosts/detail.html'
class CreateView(workflows.WorkflowView):
workflow_class = project_workflows.CreateHostsWorkflow
template_name = 'admin/hosts/create.html'
page_title = _("Create Hosts")

View File

@ -0,0 +1,118 @@
# 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
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon import messages
from horizon import workflows
from openstack_dashboard import api
from blazar_dashboard import api as blazar_api
LOG = logging.getLogger(__name__)
class SelectHostsAction(workflows.MembershipAction):
def __init__(self, request, *args, **kwargs):
super(SelectHostsAction, self).__init__(request, *args, **kwargs)
err_msg = _('Unable to get the available hosts')
default_role_field_name = self.get_default_role_field_name()
self.fields[default_role_field_name] = forms.CharField(required=False)
self.fields[default_role_field_name].initial = 'member'
field_name = self.get_member_field_name('member')
self.fields[field_name] = forms.MultipleChoiceField(required=False)
try:
nova_hosts = api.nova.host_list(request)
blazar_hosts = blazar_api.client.host_list(request)
except Exception:
exceptions.handle(request, err_msg)
nova_hostnames = []
for host in nova_hosts:
if (host.host_name not in nova_hostnames
and host.service == u'compute'):
nova_hostnames.append(host.host_name)
blazar_hostnames = []
for host in blazar_hosts:
if host.hypervisor_hostname not in blazar_hostnames:
blazar_hostnames.append(host.hypervisor_hostname)
host_names = list(set(nova_hostnames) - set(blazar_hostnames))
host_names.sort()
self.fields[field_name].choices = \
[(host_name, host_name) for host_name in host_names]
self.fields[field_name].initial = None
class Meta(object):
name = _("Select Hosts")
slug = "select_hosts"
class AddExtraCapsAction(workflows.Action):
# TODO(hiro-kobayashi): Implement this class
class Meta(object):
name = _("Extra Capabilities")
help_text = _("Not supported yet.")
slug = "add_extra_caps"
class SelectHostsStep(workflows.UpdateMembersStep):
action_class = SelectHostsAction
help_text = _("Select hosts to create")
available_list_title = _("All available hosts")
members_list_title = _("Selected hosts")
no_available_text = _("No host found.")
no_members_text = _("No host selected.")
show_roles = False
contributes = ("names",)
def contribute(self, data, context):
if data:
member_field_name = self.get_member_field_name('member')
context['names'] = data.get(member_field_name, [])
return context
class AddExtraCapsStep(workflows.Step):
# TODO(hiro-kobayashi): Implement this class
action_class = AddExtraCapsAction
help_text = _("Add extra capabilities")
show_roles = False
class CreateHostsWorkflow(workflows.Workflow):
slug = "create_hosts"
name = _("Create Hosts")
finalize_button_name = _("Create Hosts")
success_url = 'horizon:admin:hosts:index'
default_steps = (SelectHostsStep, AddExtraCapsStep)
def handle(self, request, context):
try:
for name in context['names']:
blazar_api.client.host_create(request, name=name)
messages.success(request, _('Host %s was successfully '
'created.') % name)
except Exception:
exceptions.handle(request, _('Unable to create host.'))
return False
return True

View File

@ -168,6 +168,15 @@ host_sample2 = {
}
class DummyNovaHost(object):
def __init__(self, host_name, service):
self.host_name = host_name
self.service = service
novahost_sample1 = DummyNovaHost('compute-1', 'compute')
novahost_sample2 = DummyNovaHost('compute-2', 'compute')
def data(TEST):
TEST.leases = utils.TestDataContainer()
@ -178,3 +187,8 @@ def data(TEST):
TEST.hosts.add(api.client.Host(host_sample1))
TEST.hosts.add(api.client.Host(host_sample2))
TEST.novahosts = utils.TestDataContainer()
TEST.novahosts.add(novahost_sample1)
TEST.novahosts.add(novahost_sample2)