diff options
Diffstat (limited to 'tuskar_ui')
29 files changed, 216 insertions, 191 deletions
diff --git a/tuskar_ui/api.py b/tuskar_ui/api.py index 3c66339d..11dcfbb1 100644 --- a/tuskar_ui/api.py +++ b/tuskar_ui/api.py @@ -136,10 +136,10 @@ def transform_sizing(overcloud_sizing): } for (role, flavor), sizing in overcloud_sizing.items()] -class NodeProfile(object): +class Flavor(object): def __init__(self, flavor): - """Construct node profile by wrapping flavor + """Construct by wrapping Nova flavor :param flavor: Nova flavor :type flavor: novaclient.v1_1.flavors.Flavor @@ -167,7 +167,7 @@ class NodeProfile(object): @cached_property def extras_dict(self): - """Return extra parameters of node profile + """Return extra flavor parameters :return: Nova flavor keys :rtype: dict @@ -196,12 +196,12 @@ class NodeProfile(object): metadata=extras_dict)) @classmethod - @handle_errors(_("Unable to load node profile")) - def get(cls, request, node_profile_id): - return cls(nova.flavor_get(request, node_profile_id)) + @handle_errors(_("Unable to load flavor.")) + def get(cls, request, flavor_id): + return cls(nova.flavor_get(request, flavor_id)) @classmethod - @handle_errors(_("Unable to retrieve node profile list."), []) + @handle_errors(_("Unable to retrieve flavor list."), []) def list(cls, request): return [cls(item) for item in nova.flavor_list(request)] @@ -209,7 +209,7 @@ class NodeProfile(object): @memoized.memoized @handle_errors(_("Unable to retrieve existing servers list."), []) def list_deployed_ids(cls, request): - """Get and memoize ID's of deployed node profiles.""" + """Get and memoize ID's of deployed flavors.""" servers = nova.server_list(request)[0] return set(server.flavor['id'] for server in servers) diff --git a/tuskar_ui/infrastructure/dashboard.py b/tuskar_ui/infrastructure/dashboard.py index 73876ec9..27c24848 100644 --- a/tuskar_ui/infrastructure/dashboard.py +++ b/tuskar_ui/infrastructure/dashboard.py @@ -22,7 +22,7 @@ class BasePanels(horizon.PanelGroup): panels = ( 'overcloud', 'nodes', - 'node_profiles', + 'flavors', ) diff --git a/tuskar_ui/infrastructure/node_profiles/__init__.py b/tuskar_ui/infrastructure/flavors/__init__.py index e69de29b..e69de29b 100644 --- a/tuskar_ui/infrastructure/node_profiles/__init__.py +++ b/tuskar_ui/infrastructure/flavors/__init__.py diff --git a/tuskar_ui/infrastructure/node_profiles/panel.py b/tuskar_ui/infrastructure/flavors/panel.py index 66f2f35c..ffdfdc7f 100644 --- a/tuskar_ui/infrastructure/node_profiles/panel.py +++ b/tuskar_ui/infrastructure/flavors/panel.py @@ -19,9 +19,9 @@ import horizon from tuskar_ui.infrastructure import dashboard -class NodeProfiles(horizon.Panel): - name = _("Node Profiles") - slug = "node_profiles" +class Flavors(horizon.Panel): + name = _("Flavors") + slug = "flavors" -dashboard.Infrastructure.register(NodeProfiles) +dashboard.Infrastructure.register(Flavors) diff --git a/tuskar_ui/infrastructure/node_profiles/tables.py b/tuskar_ui/infrastructure/flavors/tables.py index a68e4bfd..29b9e709 100644 --- a/tuskar_ui/infrastructure/node_profiles/tables.py +++ b/tuskar_ui/infrastructure/flavors/tables.py @@ -21,43 +21,43 @@ from openstack_dashboard.dashboards.admin.flavors \ from tuskar_ui import api -class CreateNodeProfile(flavor_tables.CreateFlavor): - verbose_name = _("New Node Profile") - url = "horizon:infrastructure:node_profiles:create" +class CreateFlavor(flavor_tables.CreateFlavor): + verbose_name = _("New Flavor") + url = "horizon:infrastructure:flavors:create" -class CreateSuggestedNodeProfile(CreateNodeProfile): +class CreateSuggestedFlavor(CreateFlavor): verbose_name = _("Create") -class DeleteNodeProfile(flavor_tables.DeleteFlavor): +class DeleteFlavor(flavor_tables.DeleteFlavor): def __init__(self, **kwargs): - super(DeleteNodeProfile, self).__init__(**kwargs) + super(DeleteFlavor, self).__init__(**kwargs) # NOTE(dtantsur): setting class attributes doesn't work # probably due to metaclass magic in actions - self.data_type_singular = _("Node Profile") - self.data_type_plural = _("Node Profiles") + self.data_type_singular = _("Flavor") + self.data_type_plural = _("Flavors") def allowed(self, request, datum=None): - """Check that action is allowed on node profile + """Check that action is allowed on flavor This is overridden method from horizon.tables.BaseAction. - :param datum: node profile we're operating on - :type datum: tuskar_ui.api.NodeProfile + :param datum: flavor we're operating on + :type datum: tuskar_ui.api.Flavor """ if datum is not None: - deployed_profiles = api.NodeProfile.list_deployed_ids( + deployed_flavors = api.Flavor.list_deployed_ids( request, _error_default=None) - if deployed_profiles is None or datum.id in deployed_profiles: + if deployed_flavors is None or datum.id in deployed_flavors: return False - return super(DeleteNodeProfile, self).allowed(request, datum) + return super(DeleteFlavor, self).allowed(request, datum) -class NodeProfilesTable(tables.DataTable): - name = tables.Column('name', verbose_name=_('Node Profile'), - link="horizon:infrastructure:node_profiles:details") +class FlavorsTable(tables.DataTable): + name = tables.Column('name', verbose_name=_('Flavor'), + link="horizon:infrastructure:flavors:details") arch = tables.Column('cpu_arch', verbose_name=_('Architecture')) vcpus = tables.Column('vcpus', verbose_name=_('CPUs')) ram = tables.Column(flavor_tables.get_size, @@ -73,15 +73,15 @@ class NodeProfilesTable(tables.DataTable): verbose_name=_('Deploy Ramdisk Image ID')) class Meta: - name = "node_profiles" - verbose_name = _("Node Profiles") - table_actions = (CreateNodeProfile, - DeleteNodeProfile, + name = "flavors" + verbose_name = _("Flavors") + table_actions = (CreateFlavor, + DeleteFlavor, flavor_tables.FlavorFilterAction) - row_actions = (DeleteNodeProfile,) + row_actions = (DeleteFlavor,) -class NodeProfileRolesTable(tables.DataTable): +class FlavorRolesTable(tables.DataTable): name = tables.Column('name', verbose_name=_('Role Name')) def __init__(self, request, *args, **kwargs): @@ -96,10 +96,10 @@ class NodeProfileRolesTable(tables.DataTable): count_getter, verbose_name=_("Instances Count") ) - super(NodeProfileRolesTable, self).__init__(request, *args, **kwargs) + super(FlavorRolesTable, self).__init__(request, *args, **kwargs) -class ProfileSuggestionsTable(tables.DataTable): +class FlavorSuggestionsTable(tables.DataTable): arch = tables.Column('cpu_arch', verbose_name=_('Architecture')) vcpus = tables.Column('vcpus', verbose_name=_('CPUs')) ram = tables.Column(flavor_tables.get_size, verbose_name=_('Memory'), @@ -108,7 +108,7 @@ class ProfileSuggestionsTable(tables.DataTable): verbose_name=_('Disk'), attrs={'data-type': 'size'}) class Meta: - name = "profile_suggestions" - verbose_name = _("Profile Suggestions") + name = "flavor_suggestions" + verbose_name = _("Flavor Suggestions") table_actions = () - row_actions = (CreateSuggestedNodeProfile,) + row_actions = (CreateSuggestedFlavor,) diff --git a/tuskar_ui/infrastructure/node_profiles/tabs.py b/tuskar_ui/infrastructure/flavors/tabs.py index 40aaf950..772c40a3 100644 --- a/tuskar_ui/infrastructure/node_profiles/tabs.py +++ b/tuskar_ui/infrastructure/flavors/tabs.py @@ -16,41 +16,41 @@ from django.utils.translation import ugettext_lazy as _ import horizon.tabs from tuskar_ui import api -from tuskar_ui.infrastructure.node_profiles import tables +from tuskar_ui.infrastructure.flavors import tables def _get_unmatched_suggestions(request): unmatched_suggestions = [] - profile_suggestions = [ProfileSuggestion.from_node_profile(node_profile) - for node_profile in api.NodeProfile.list(request)] + flavor_suggestions = [FlavorSuggestion.from_flavor(flavor) + for flavor in api.Flavor.list(request)] for node in api.Node.list(request): - node_suggestion = ProfileSuggestion.from_node(node) - for profile_suggestion in profile_suggestions: - if profile_suggestion == node_suggestion: + node_suggestion = FlavorSuggestion.from_node(node) + for flavor_suggestion in flavor_suggestions: + if flavor_suggestion == node_suggestion: break else: unmatched_suggestions.append(node_suggestion) return unmatched_suggestions -def get_profile_suggestions(request): +def get_flavor_suggestions(request): return set(_get_unmatched_suggestions(request)) -class NodeProfilesTab(horizon.tabs.TableTab): - name = _("Node Profiles") - slug = 'node_profiles' - table_classes = (tables.NodeProfilesTable,) +class FlavorsTab(horizon.tabs.TableTab): + name = _("Flavors") + slug = 'flavors' + table_classes = (tables.FlavorsTable,) template_name = ("horizon/common/_detail_table.html") preload = False - def get_node_profiles_data(self): - node_profiles = api.NodeProfile.list(self.request) - node_profiles.sort(key=lambda np: (np.vcpus, np.ram, np.disk)) - return node_profiles + def get_flavors_data(self): + flavors = api.Flavor.list(self.request) + flavors.sort(key=lambda np: (np.vcpus, np.ram, np.disk)) + return flavors -class ProfileSuggestion(object): +class FlavorSuggestion(object): """Describe node parameters in a way that is easy to compare.""" def __init__(self, vcpus=None, ram=None, disk=None, cpu_arch=None, @@ -72,11 +72,11 @@ class ProfileSuggestion(object): ) @classmethod - def from_node_profile(cls, node_profile): + def from_flavor(cls, flavor): return cls( - vcpus=node_profile.vcpus, - ram_bytes=node_profile.ram_bytes, - disk_bytes=node_profile.disk_bytes, + vcpus=flavor.vcpus, + ram_bytes=flavor.ram_bytes, + disk_bytes=flavor.disk_bytes, # TODO(rdopieralski) Add architecture when available. ) @@ -120,21 +120,21 @@ class ProfileSuggestion(object): ) -class ProfileSuggestionsTab(horizon.tabs.TableTab): - name = _("Profile Suggestions") - slug = 'profile_suggestions' - table_classes = (tables.ProfileSuggestionsTable,) +class FlavorSuggestionsTab(horizon.tabs.TableTab): + name = _("Flavor Suggestions") + slug = 'flavor_suggestions' + table_classes = (tables.FlavorSuggestionsTable,) template_name = ("horizon/common/_detail_table.html") preload = False - def get_profile_suggestions_data(self): - return list(get_profile_suggestions(self.request)) + def get_flavor_suggestions_data(self): + return list(get_flavor_suggestions(self.request)) -class NodeProfileTabs(horizon.tabs.TabGroup): - slug = 'node_profile_tabs' +class FlavorTabs(horizon.tabs.TabGroup): + slug = 'flavor_tabs' tabs = ( - NodeProfilesTab, - ProfileSuggestionsTab, + FlavorsTab, + FlavorSuggestionsTab, ) sticky = True diff --git a/tuskar_ui/infrastructure/node_profiles/templates/node_profiles/create.html b/tuskar_ui/infrastructure/flavors/templates/flavors/create.html index 23b0bcf8..4fedb05a 100644 --- a/tuskar_ui/infrastructure/node_profiles/templates/node_profiles/create.html +++ b/tuskar_ui/infrastructure/flavors/templates/flavors/create.html @@ -1,9 +1,9 @@ {% extends 'infrastructure/base.html' %} {% load i18n %} -{% block title %}{% trans "Create Node Profile" %}{% endblock %} +{% block title %}{% trans "Create Flavor" %}{% endblock %} {% block page_header %} - {% include "horizon/common/_page_header.html" with title=_("Create Node Profile") %} + {% include "horizon/common/_page_header.html" with title=_("Create Flavor") %} {% endblock page_header %} {% block main %} diff --git a/tuskar_ui/infrastructure/node_profiles/templates/node_profiles/details.html b/tuskar_ui/infrastructure/flavors/templates/flavors/details.html index 628a0991..a6331c18 100644 --- a/tuskar_ui/infrastructure/node_profiles/templates/node_profiles/details.html +++ b/tuskar_ui/infrastructure/flavors/templates/flavors/details.html @@ -1,9 +1,9 @@ {% extends 'infrastructure/base.html' %} {% load i18n %} -{% block title %}{% trans 'Node Profile Details' %}{% endblock %} +{% block title %}{% trans 'Flavor Details' %}{% endblock %} {% block page_header %} - {% include 'horizon/common/_page_header.html' with title=_('Node Profile Details') %} + {% include 'horizon/common/_page_header.html' with title=_('Flavor Details') %} {% endblock page_header %} {% block main %} @@ -12,13 +12,13 @@ <h4>{% trans "Hardware Info" %}</h4> <dl class="clearfix"> <dt>{% trans "Architecture" %}</dt> - <dd>{{ node_profile.cpu_arch|default:"—" }}</dd> + <dd>{{ flavor.cpu_arch|default:"—" }}</dd> <dt>{% trans "CPUs" %}</dt> - <dd>{{ node_profile.vcpus|default:"—" }}</dd> + <dd>{{ flavor.vcpus|default:"—" }}</dd> <dt>{% trans "Memory" %}</dt> - <dd>{{ node_profile.ram_bytes|filesizeformat|default:"—" }}</dd> + <dd>{{ flavor.ram_bytes|filesizeformat|default:"—" }}</dd> <dt>{% trans "Disk" %}</dt> - <dd>{{ node_profile.disk_bytes|filesizeformat|default:"—" }}</dd> + <dd>{{ flavor.disk_bytes|filesizeformat|default:"—" }}</dd> </dl> <h4>{% trans "Deploy Images" %}</h4> <dl class="clearfix"> diff --git a/tuskar_ui/infrastructure/node_profiles/templates/node_profiles/index.html b/tuskar_ui/infrastructure/flavors/templates/flavors/index.html index 74e601d1..663a6237 100644 --- a/tuskar_ui/infrastructure/node_profiles/templates/node_profiles/index.html +++ b/tuskar_ui/infrastructure/flavors/templates/flavors/index.html @@ -1,10 +1,10 @@ {% extends 'infrastructure/base.html' %} {% load i18n %} {% load url from future %} -{% block title %}{% trans 'Node Profiles' %}{% endblock %} +{% block title %}{% trans 'Flavors' %}{% endblock %} {% block page_header %} - {% include 'horizon/common/_domain_page_header.html' with title=_('Node Profiles') %} + {% include 'horizon/common/_domain_page_header.html' with title=_('Flavors') %} {% endblock page_header %} {% block main %} diff --git a/tuskar_ui/infrastructure/node_profiles/tests.py b/tuskar_ui/infrastructure/flavors/tests.py index 34b2b668..1b43d125 100644 --- a/tuskar_ui/infrastructure/node_profiles/tests.py +++ b/tuskar_ui/infrastructure/flavors/tests.py @@ -30,10 +30,10 @@ from tuskar_ui.test.test_data import tuskar_data TEST_DATA = utils.TestDataContainer() tuskar_data.data(TEST_DATA) INDEX_URL = urlresolvers.reverse( - 'horizon:infrastructure:node_profiles:index') + 'horizon:infrastructure:flavors:index') CREATE_URL = urlresolvers.reverse( - 'horizon:infrastructure:node_profiles:create') -DETAILS_VIEW = 'horizon:infrastructure:node_profiles:details' + 'horizon:infrastructure:flavors:create') +DETAILS_VIEW = 'horizon:infrastructure:flavors:details' @contextlib.contextmanager @@ -49,7 +49,7 @@ def _prepare_create(): 'kernel_image_id': images[0].id, 'ramdisk_image_id': images[1].id} with contextlib.nested( - patch('tuskar_ui.api.NodeProfile.create', + patch('tuskar_ui.api.Flavor.create', return_value=flavor), patch('openstack_dashboard.api.glance.image_list_detailed', return_value=(TEST_DATA.glanceclient_images.list(), False)), @@ -60,7 +60,7 @@ def _prepare_create(): yield mocks[0], data -class NodeProfilesTest(test.BaseAdminViewTests): +class FlavorsTest(test.BaseAdminViewTests): def test_index(self): with contextlib.nested( @@ -74,7 +74,7 @@ class NodeProfilesTest(test.BaseAdminViewTests): self.assertEqual(servers_mock.call_count, 1) self.assertTemplateUsed(res, - 'infrastructure/node_profiles/index.html') + 'infrastructure/flavors/index.html') def test_index_recoverable_failure(self): with patch('openstack_dashboard.api.nova.flavor_list', @@ -89,7 +89,7 @@ class NodeProfilesTest(test.BaseAdminViewTests): res = self.client.get(CREATE_URL) self.assertEqual(mock.call_count, 2) self.assertTemplateUsed(res, - 'infrastructure/node_profiles/create.html') + 'infrastructure/flavors/create.html') def test_create_get_recoverable_failure(self): with patch('openstack_dashboard.api.glance.image_list_detailed', @@ -119,7 +119,7 @@ class NodeProfilesTest(test.BaseAdminViewTests): def test_delete_ok(self): flavors = TEST_DATA.novaclient_flavors.list() - data = {'action': 'node_profiles__delete', + data = {'action': 'flavors__delete', 'object_ids': [flavors[0].id, flavors[1].id]} with contextlib.nested( patch('openstack_dashboard.api.nova.flavor_delete'), @@ -146,7 +146,7 @@ class NodeProfilesTest(test.BaseAdminViewTests): 'status': 'ACTIVE', 'flavor': {'id': flavors[0].id}} ) - data = {'action': 'node_profiles__delete', + data = {'action': 'flavors__delete', 'object_ids': [flavors[0].id, flavors[1].id]} with contextlib.nested( patch('openstack_dashboard.api.nova.flavor_delete'), @@ -165,14 +165,14 @@ class NodeProfilesTest(test.BaseAdminViewTests): self.assertEqual(server_list_mock.call_count, 1) def test_details_no_overcloud(self): - flavor = api.NodeProfile(TEST_DATA.novaclient_flavors.first()) + flavor = api.Flavor(TEST_DATA.novaclient_flavors.first()) images = TEST_DATA.glanceclient_images.list()[:2] roles = TEST_DATA.tuskarclient_overcloud_roles.list() roles[0].flavor_id = flavor.id with contextlib.nested( patch('openstack_dashboard.api.glance.image_get', side_effect=images), - patch('tuskar_ui.api.NodeProfile.get', + patch('tuskar_ui.api.Flavor.get', return_value=flavor), patch('tuskar_ui.api.OvercloudRole.list', return_value=roles), @@ -186,10 +186,10 @@ class NodeProfilesTest(test.BaseAdminViewTests): self.assertEqual(roles_mock.call_count, 1) self.assertEqual(overcloud_mock.call_count, 1) self.assertTemplateUsed(res, - 'infrastructure/node_profiles/details.html') + 'infrastructure/flavors/details.html') def test_details(self): - flavor = api.NodeProfile(TEST_DATA.novaclient_flavors.first()) + flavor = api.Flavor(TEST_DATA.novaclient_flavors.first()) images = TEST_DATA.glanceclient_images.list()[:2] roles = TEST_DATA.tuskarclient_overcloud_roles.list() roles[0].flavor_id = flavor.id @@ -197,7 +197,7 @@ class NodeProfilesTest(test.BaseAdminViewTests): with contextlib.nested( patch('openstack_dashboard.api.glance.image_get', side_effect=images), - patch('tuskar_ui.api.NodeProfile.get', + patch('tuskar_ui.api.Flavor.get', return_value=flavor), patch('tuskar_ui.api.OvercloudRole.list', return_value=roles), @@ -216,4 +216,4 @@ class NodeProfilesTest(test.BaseAdminViewTests): self.assertEqual(count_mock.call_count, 1) self.assertListEqual(count_mock.call_args_list, [call(roles[0])]) self.assertTemplateUsed(res, - 'infrastructure/node_profiles/details.html') + 'infrastructure/flavors/details.html') diff --git a/tuskar_ui/infrastructure/node_profiles/urls.py b/tuskar_ui/infrastructure/flavors/urls.py index 0428e3ec..7f78dc45 100644 --- a/tuskar_ui/infrastructure/node_profiles/urls.py +++ b/tuskar_ui/infrastructure/flavors/urls.py @@ -14,11 +14,11 @@ from django.conf import urls -from tuskar_ui.infrastructure.node_profiles import views +from tuskar_ui.infrastructure.flavors import views urlpatterns = urls.patterns( - 'tuskar_ui.infrastructure.node_profiles.views', + 'tuskar_ui.infrastructure.flavors.views', urls.url(r'^$', views.IndexView.as_view(), name='index'), urls.url(r'^create/(?P<suggestion_id>[^/]+)$', views.CreateView.as_view(), name='create'), diff --git a/tuskar_ui/infrastructure/node_profiles/views.py b/tuskar_ui/infrastructure/flavors/views.py index c95f1179..ed8dc3d5 100644 --- a/tuskar_ui/infrastructure/node_profiles/views.py +++ b/tuskar_ui/infrastructure/flavors/views.py @@ -21,9 +21,9 @@ import horizon.tabs import horizon.workflows import tuskar_ui.api -from tuskar_ui.infrastructure.node_profiles import tables -from tuskar_ui.infrastructure.node_profiles import tabs -from tuskar_ui.infrastructure.node_profiles import workflows +from tuskar_ui.infrastructure.flavors import tables +from tuskar_ui.infrastructure.flavors import tabs +from tuskar_ui.infrastructure.flavors import workflows def image_get(request, image_id, error_message): @@ -35,20 +35,20 @@ def image_get(request, image_id, error_message): class IndexView(horizon.tabs.TabbedTableView): - tab_group_class = tabs.NodeProfileTabs - template_name = 'infrastructure/node_profiles/index.html' + tab_group_class = tabs.FlavorTabs + template_name = 'infrastructure/flavors/index.html' class CreateView(horizon.workflows.WorkflowView): - workflow_class = workflows.CreateNodeProfile - template_name = 'infrastructure/node_profiles/create.html' + workflow_class = workflows.CreateFlavor + template_name = 'infrastructure/flavors/create.html' def get_initial(self): suggestion_id = self.kwargs.get('suggestion_id') if not suggestion_id: return super(CreateView, self).get_initial() node = tuskar_ui.api.Node.get(self.request, suggestion_id) - suggestion = tabs.ProfileSuggestion.from_node(node) + suggestion = tabs.FlavorSuggestion.from_node(node) return { 'name': suggestion.name, 'vcpus': suggestion.vcpus, @@ -59,25 +59,25 @@ class CreateView(horizon.workflows.WorkflowView): class DetailView(horizon.tables.DataTableView): - table_class = tables.NodeProfileRolesTable - template_name = 'infrastructure/node_profiles/details.html' - error_redirect = reverse_lazy('horizon:infrastructure:node_profiles:index') + table_class = tables.FlavorRolesTable + template_name = 'infrastructure/flavors/details.html' + error_redirect = reverse_lazy('horizon:infrastructure:flavors:index') def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) - context['node_profile'] = tuskar_ui.api.NodeProfile.get( + context['flavor'] = tuskar_ui.api.Flavor.get( self.request, kwargs.get('flavor_id'), _error_redirect=self.error_redirect ) context['kernel_image'] = image_get( self.request, - context['node_profile'].kernel_image_id, + context['flavor'].kernel_image_id, error_message=_("Cannot get kernel image details") ) context['ramdisk_image'] = image_get( self.request, - context['node_profile'].ramdisk_image_id, + context['flavor'].ramdisk_image_id, error_message=_("Cannot get ramdisk image details") ) return context diff --git a/tuskar_ui/infrastructure/node_profiles/workflows.py b/tuskar_ui/infrastructure/flavors/workflows.py index 7d7f8336..0ea91c04 100644 --- a/tuskar_ui/infrastructure/node_profiles/workflows.py +++ b/tuskar_ui/infrastructure/flavors/workflows.py @@ -24,7 +24,7 @@ from openstack_dashboard.dashboards.admin.flavors \ from tuskar_ui import api -class CreateNodeProfileAction(flavor_workflows.CreateFlavorInfoAction): +class CreateFlavorAction(flavor_workflows.CreateFlavorInfoAction): arch = fields.ChoiceField(choices=(('i386', 'i386'), ('amd64', 'amd64')), label=_("Architecture")) kernel_image_id = fields.ChoiceField(choices=(), @@ -33,7 +33,7 @@ class CreateNodeProfileAction(flavor_workflows.CreateFlavorInfoAction): label=_("Deploy Ramdisk Image")) def __init__(self, *args, **kwrds): - super(CreateNodeProfileAction, self).__init__(*args, **kwrds) + super(CreateFlavorAction, self).__init__(*args, **kwrds) try: kernel_images = glance.image_list_detailed( self.request, @@ -62,14 +62,14 @@ class CreateNodeProfileAction(flavor_workflows.CreateFlavorInfoAction): del self.fields['flavor_id'] class Meta: - name = _("Node Profile") + name = _("Flavor") # FIXME(dtantsur): maybe better help text? help_text = _("From here you can create a new " - "node profile to organize instance resources.") + "flavor to organize instance resources.") -class CreateNodeProfileStep(workflows.Step): - action_class = CreateNodeProfileAction +class CreateFlavorStep(workflows.Step): + action_class = CreateFlavorAction contributes = ("name", "vcpus", "memory_mb", @@ -79,18 +79,18 @@ class CreateNodeProfileStep(workflows.Step): "ramdisk_image_id") -class CreateNodeProfile(flavor_workflows.CreateFlavor): - slug = "create_node_profile" - name = _("Create Node Profile") - finalize_button_name = _("Create Node Profile") - success_message = _('Created new node profile "%s".') - failure_message = _('Unable to create node profile "%s".') - success_url = "horizon:infrastructure:node_profiles:index" - default_steps = (CreateNodeProfileStep,) +class CreateFlavor(flavor_workflows.CreateFlavor): + slug = "create_flavor" + name = _("Create Flavor") + finalize_button_name = _("Create Flavor") + success_message = _('Created new flavor "%s".') + failure_message = _('Unable to create flavor "%s".') + success_url = "horizon:infrastructure:flavors:index" + default_steps = (CreateFlavorStep,) def handle(self, request, data): try: - self.object = api.NodeProfile.create( + self.object = api.Flavor.create( request, name=data['name'], memory=data['memory_mb'], @@ -101,6 +101,6 @@ class CreateNodeProfile(flavor_workflows.CreateFlavor): ramdisk_image_id=data['ramdisk_image_id'] ) except Exception: - exceptions.handle(request, _("Unable to create node profile")) + exceptions.handle(request, _("Unable to create flavor")) return False return True diff --git a/tuskar_ui/infrastructure/nodes/tables.py b/tuskar_ui/infrastructure/nodes/tables.py index edd5c5bc..a40b6579 100644 --- a/tuskar_ui/infrastructure/nodes/tables.py +++ b/tuskar_ui/infrastructure/nodes/tables.py @@ -35,6 +35,21 @@ class DeleteNode(tables.BatchAction): api.Node.delete(request, obj_id) +class NodeFilterAction(tables.FilterAction): + def filter(self, table, nodes, filter_string): + """Really naive case-insensitive search.""" + q = filter_string.lower() + + def comp(node): + return any(q in attr for attr in + (node.ip_address, + node.properties['cpu'], + node.properties['ram'], + node.properties['local_disk'],)) + + return filter(comp, nodes) + + class NodesTable(tables.DataTable): node = tables.Column(lambda node: node.driver_info['ip_address'], link="horizon:infrastructure:nodes:detail", @@ -88,7 +103,8 @@ class FreeNodesTable(NodesTable): class Meta: name = "free_nodes" verbose_name = _("Free Nodes") - table_actions = (DeleteNode,) + table_actions = (DeleteNode, + NodeFilterAction,) row_actions = (DeleteNode,) @@ -109,7 +125,7 @@ class DeployedNodesTable(NodesTable): class Meta: name = "deployed_nodes" verbose_name = _("Deployed Nodes") - table_actions = () + table_actions = (NodeFilterAction,) row_actions = () columns = ('node', 'deployment_role', 'capacity', 'architecture', 'cpu', 'ram', 'local_disk', 'health', 'power_state') diff --git a/tuskar_ui/infrastructure/overcloud/forms.py b/tuskar_ui/infrastructure/overcloud/forms.py index e9b9efc2..33a38e5b 100644 --- a/tuskar_ui/infrastructure/overcloud/forms.py +++ b/tuskar_ui/infrastructure/overcloud/forms.py @@ -63,7 +63,7 @@ class OvercloudRoleForm(horizon.forms.SelfHandlingForm): widget=django.forms.TextInput( attrs={'readonly': 'readonly', 'disabled': 'disabled'})) flavor_id = django.forms.ChoiceField( - label=_("Node Profile"), required=False, choices=()) + label=_("Flavor"), required=False, choices=()) def __init__(self, *args, **kwargs): super(OvercloudRoleForm, self).__init__(*args, **kwargs) diff --git a/tuskar_ui/infrastructure/overcloud/tables.py b/tuskar_ui/infrastructure/overcloud/tables.py index d4fd2a56..c4068243 100644 --- a/tuskar_ui/infrastructure/overcloud/tables.py +++ b/tuskar_ui/infrastructure/overcloud/tables.py @@ -49,7 +49,8 @@ class ConfigurationTable(tables.DataTable): class LogTable(tables.DataTable): timestamp = tables.Column('event_time', - verbose_name=_("Timestamp")) + verbose_name=_("Timestamp"), + attrs={'data-type': 'timestamp'}) resource_name = tables.Column('resource_name', verbose_name=_("Resource Name")) resource_status = tables.Column('resource_status', diff --git a/tuskar_ui/infrastructure/overcloud/tabs.py b/tuskar_ui/infrastructure/overcloud/tabs.py index ee1fb15a..fd255456 100644 --- a/tuskar_ui/infrastructure/overcloud/tabs.py +++ b/tuskar_ui/infrastructure/overcloud/tabs.py @@ -73,7 +73,7 @@ class OverviewTab(tabs.Tab): return { 'overcloud': overcloud, 'roles': role_data, - 'progress': progress, + 'progress': max(5, progress), 'dashboard_urls': overcloud.dashboard_urls, 'last_failed_events': last_failed_events, } diff --git a/tuskar_ui/infrastructure/overcloud/templates/overcloud/_detail_overview.html b/tuskar_ui/infrastructure/overcloud/templates/overcloud/_detail_overview.html index 562b52d6..92407dcf 100644 --- a/tuskar_ui/infrastructure/overcloud/templates/overcloud/_detail_overview.html +++ b/tuskar_ui/infrastructure/overcloud/templates/overcloud/_detail_overview.html @@ -57,15 +57,15 @@ {% if not dashboard_urls and not overcloud.is_deploying and not overcloud.is_failed and not overcloud.is_deleting %} <div class="row-fluid"> <div class="span8"> - <p>Your Overcloud has been successfully deployed. It needs to be initialized first, before you will be able to use it. + <p>Your OpenStack Deployment has been successfully deployed. It needs to be initialized before you will be able to use it. <p/> <button type="button" class="btn btn-primary" data-toggle="collapse" data-target="#demo"> Initialize </button> <div id="demo" class="collapse"> - <p>You need to run below commands on the machine where you have tripleo source code and - direct access through ssh to control node ({{overcloud.keystone_ip}}) + <p>You need to run the below commands on the machine where you have tripleo source code and + direct access through ssh to control node ({{overcloud.keystone_ip}}). </p> <pre> export TRIPLEO_ROOT=~/tripleo @@ -179,7 +179,7 @@ </p> {% endfor %} {% else %} - You need to initialize you Overcloud first. + You need to initialize your OpenStack Deployment first. {% endif %} </div> </div> diff --git a/tuskar_ui/infrastructure/overcloud/templates/overcloud/detail.html b/tuskar_ui/infrastructure/overcloud/templates/overcloud/detail.html index 13ed2541..ba091bb5 100644 --- a/tuskar_ui/infrastructure/overcloud/templates/overcloud/detail.html +++ b/tuskar_ui/infrastructure/overcloud/templates/overcloud/detail.html @@ -25,12 +25,14 @@ <i class="icon-fire icon-white"></i> {% trans "Undeploy" %} </a> + {% comment %} no scaling for Icehouse, uncomment when ready <a href="{% url 'horizon:infrastructure:overcloud:scale' overcloud.id %}" class="btn ajax-modal {% if not overcloud.is_deployed %}disabled{% endif %}"> <i class="icon-resize-full"></i> {% trans "Scale deployment" %} </a> + {% endcomment %} </div> {{ tab_group.render }} </div> diff --git a/tuskar_ui/infrastructure/overcloud/templates/overcloud/node_counts.html b/tuskar_ui/infrastructure/overcloud/templates/overcloud/node_counts.html index e6940367..cb4956a4 100644 --- a/tuskar_ui/infrastructure/overcloud/templates/overcloud/node_counts.html +++ b/tuskar_ui/infrastructure/overcloud/templates/overcloud/node_counts.html @@ -1,10 +1,11 @@ {% load i18n %} +{% load url from future %} {% include 'horizon/common/_form_errors.html' with form=form %} <table class="table"> <tr> <th>{% trans "Name" %}</th> - <th>{% trans "Node profiles" %}</th> + <th>{% trans "Flavors" %}</th> <th>{% trans "Nodes" %}</th> {% if show_change %} <th>{% trans "Change" %}</th> @@ -32,9 +33,9 @@ (<a href="{% url 'horizon:infrastructure:overcloud:role_edit' role_id %}" class="ajax-modal" - >{% trans "Add a node profile" %}</a>) + >{% trans "Add a flavor" %}</a>) {% else %} - ({% trans "No node profile" %}) + ({% trans "No flavor" %}) {% endif %} </td> <td> diff --git a/tuskar_ui/infrastructure/overcloud/templates/overcloud/overcloud_role.html b/tuskar_ui/infrastructure/overcloud/templates/overcloud/overcloud_role.html index dc78d192..37905bdc 100644 --- a/tuskar_ui/infrastructure/overcloud/templates/overcloud/overcloud_role.html +++ b/tuskar_ui/infrastructure/overcloud/templates/overcloud/overcloud_role.html @@ -12,12 +12,13 @@ <div class="span12"> <p><strong>{% blocktrans count counter=nodes|length %}{{ counter }} instance{% plural %}{{ counter }} instances{% endblocktrans %}</strong></p> <dl> - <dt>node profile</dt> + <dt>{% trans 'Flavor' %}</dt> <dd><em>{{ flavor.name }}</em> {{ flavor.get_keys.cpu_arch }} | {{ flavor.vcpus }} {% trans "CPU" %} | {{ flavor.ram }} {% trans "MB RAM" %} | {{ flavor.disk }} {% trans "GB HDD" %}</dd> - <dt>image</dt> + <dt>{% trans 'Image' %}</dt> <dd>{{ image_name }}</dd> </dl> + <h3>{% trans 'Nodes' %}</h3> {{ table.render }} </div> </div> diff --git a/tuskar_ui/infrastructure/overcloud/tests.py b/tuskar_ui/infrastructure/overcloud/tests.py index 8f8c193f..a995744f 100644 --- a/tuskar_ui/infrastructure/overcloud/tests.py +++ b/tuskar_ui/infrastructure/overcloud/tests.py @@ -145,6 +145,8 @@ class OvercloudTests(test.BaseAdminViewTests): self.assertTemplateUsed( res, 'infrastructure/_fullscreen_workflow_base.html') self.assertTemplateUsed( + res, 'infrastructure/overcloud/node_counts.html') + self.assertTemplateUsed( res, 'infrastructure/overcloud/undeployed_overview.html') def test_create_post(self): diff --git a/tuskar_ui/infrastructure/overcloud/views.py b/tuskar_ui/infrastructure/overcloud/views.py index d389fc98..1ee0982e 100644 --- a/tuskar_ui/infrastructure/overcloud/views.py +++ b/tuskar_ui/infrastructure/overcloud/views.py @@ -172,7 +172,7 @@ class OvercloudRoleView(horizon_tables.DataTableView, try: context['flavor'] = nova.flavor_get(self.request, role.flavor_id) except Exception: - msg = _('Unable to retrieve node profile.') + msg = _('Unable to retrieve flavor.') horizon.exceptions.handle(self.request, msg) return context diff --git a/tuskar_ui/infrastructure/overcloud/workflows/scale_node_counts.py b/tuskar_ui/infrastructure/overcloud/workflows/scale_node_counts.py index a32bb88f..751da7d3 100644 --- a/tuskar_ui/infrastructure/overcloud/workflows/scale_node_counts.py +++ b/tuskar_ui/infrastructure/overcloud/workflows/scale_node_counts.py @@ -29,7 +29,7 @@ class Step(undeployed_overview.Step): template_name = 'infrastructure/overcloud/scale_node_counts.html' def prepare_action_context(self, request, context): - for (role_id, profile_id), count in context['role_counts'].items(): - name = 'count__%s__%s' % (role_id, profile_id) + for (role_id, flavor_id), count in context['role_counts'].items(): + name = 'count__%s__%s' % (role_id, flavor_id) context[name] = count return context diff --git a/tuskar_ui/infrastructure/overcloud/workflows/undeployed.py b/tuskar_ui/infrastructure/overcloud/workflows/undeployed.py index c919cf2e..a3052337 100644 --- a/tuskar_ui/infrastructure/overcloud/workflows/undeployed.py +++ b/tuskar_ui/infrastructure/overcloud/workflows/undeployed.py @@ -60,6 +60,7 @@ class Workflow(DeploymentValidationMixin, horizon.workflows.Workflow): undeployed_configuration.Step, ) finalize_button_name = _("Deploy") + success_message = _("OpenStack deployment launched") success_url = 'horizon:infrastructure:overcloud:index' def handle(self, request, context): diff --git a/tuskar_ui/infrastructure/overcloud/workflows/undeployed_overview.py b/tuskar_ui/infrastructure/overcloud/workflows/undeployed_overview.py index 809a53e0..8971f05c 100644 --- a/tuskar_ui/infrastructure/overcloud/workflows/undeployed_overview.py +++ b/tuskar_ui/infrastructure/overcloud/workflows/undeployed_overview.py @@ -22,17 +22,17 @@ from tuskar_ui import api import tuskar_ui.forms -def get_role_id_and_profile_id_from_field_name(field_name): - """Extract the ids of overcloud role and node profile from the field +def get_role_id_and_flavor_id_from_field_name(field_name): + """Extract the ids of overcloud role and flavor from the field name. """ - _count, role_id, profile_id = field_name.split('__', 2) - return role_id, profile_id + _count, role_id, flavor_id = field_name.split('__', 2) + return role_id, flavor_id -def get_field_name_from_role_id_and_profile_id(role_id, profile_id=''): - """Compose the ids of overcloud role and node profile into a field name.""" - return 'count__%s__%s' % (role_id, profile_id) +def get_field_name_from_role_id_and_flavor_id(role_id, flavor_id=''): + """Compose the ids of overcloud role and flavor into a field name.""" + return 'count__%s__%s' % (role_id, flavor_id) class Action(horizon.workflows.Action): @@ -40,7 +40,7 @@ class Action(horizon.workflows.Action): slug = 'undeployed_overview' name = _("Overview") - def _get_profile_names(self): + def _get_flavor_names(self): # Get all flavors in one call, instead of getting them one by one. try: flavors = horizon_api.nova.flavor_list(self.request, None) @@ -50,21 +50,21 @@ class Action(horizon.workflows.Action): flavors = [] return dict((str(flavor.id), flavor.name) for flavor in flavors) - def _get_profiles(self, role, profile_names): - # TODO(rdopieralski) Get a list of hardware profiles for each - # role here, when we support multiple profiles per role. - if role.flavor_id and role.flavor_id in profile_names: - profiles = [( + def _get_flavors(self, role, flavor_names): + # TODO(rdopieralski) Get a list of flavors for each + # role here, when we support multiple flavors per role. + if role.flavor_id and role.flavor_id in flavor_names: + flavors = [( role.flavor_id, - profile_names[role.flavor_id], + flavor_names[role.flavor_id], )] else: - profiles = [] - return profiles + flavors = [] + return flavors def __init__(self, *args, **kwargs): super(Action, self).__init__(*args, **kwargs) - profile_names = self._get_profile_names() + flavor_names = self._get_flavor_names() for role in self._get_roles(): if role.name == 'Controller': initial = 1 @@ -72,16 +72,16 @@ class Action(horizon.workflows.Action): else: initial = 0 attrs = {} - profiles = self._get_profiles(role, profile_names) - if not profiles: - name = get_field_name_from_role_id_and_profile_id(str(role.id)) + flavors = self._get_flavors(role, flavor_names) + if not flavors: + name = get_field_name_from_role_id_and_flavor_id(str(role.id)) attrs = {'readonly': 'readonly'} self.fields[name] = django.forms.IntegerField( label='', initial=initial, min_value=initial, widget=tuskar_ui.forms.NumberPickerInput(attrs=attrs)) - for profile_id, label in profiles: - name = get_field_name_from_role_id_and_profile_id( - str(role.id), profile_id) + for flavor_id, label in flavors: + name = get_field_name_from_role_id_and_flavor_id( + str(role.id), flavor_id) self.fields[name] = django.forms.IntegerField( label=label, initial=initial, min_value=initial, widget=tuskar_ui.forms.NumberPickerInput(attrs=attrs)) @@ -93,7 +93,7 @@ class Action(horizon.workflows.Action): role.id, role.name, list(tuskar_ui.forms.fieldset( - self, prefix=get_field_name_from_role_id_and_profile_id( + self, prefix=get_field_name_from_role_id_and_flavor_id( str(role.id)))), ) @@ -106,10 +106,10 @@ class Action(horizon.workflows.Action): for key, value in self.cleaned_data.iteritems(): if not key.startswith('count_'): continue - role_id, profile = get_role_id_and_profile_id_from_field_name(key) - if int(value) and not profile: + role_id, flavor = get_role_id_and_flavor_id_from_field_name(key) + if int(value) and not flavor: raise django.forms.ValidationError( - _("Can't deploy nodes without a node profile assigned.")) + _("Can't deploy nodes without a flavor assigned.")) return self.cleaned_data @@ -128,7 +128,7 @@ class Step(horizon.workflows.Step): for key, value in data.iteritems(): if not key.startswith('count_'): continue - count, role_id, profile = key.split('__', 2) - counts[role_id, profile] = int(value) + count, role_id, flavor = key.split('__', 2) + counts[role_id, flavor] = int(value) context['role_counts'] = counts return context diff --git a/tuskar_ui/infrastructure/static/infrastructure/less/infrastructure.less b/tuskar_ui/infrastructure/static/infrastructure/less/infrastructure.less index 113d1e98..bd5ed043 100644 --- a/tuskar_ui/infrastructure/static/infrastructure/less/infrastructure.less +++ b/tuskar_ui/infrastructure/static/infrastructure/less/infrastructure.less @@ -26,3 +26,8 @@ .fullscreen-workflow-body { overflow: auto; } + +// hide the project switcher from the top bar +#tenant_switcher { + visibility: hidden; +} diff --git a/tuskar_ui/test/api_tests/tuskar_tests.py b/tuskar_ui/test/api_tests/tuskar_tests.py index 609bf158..aa4e5c96 100644 --- a/tuskar_ui/test/api_tests/tuskar_tests.py +++ b/tuskar_ui/test/api_tests/tuskar_tests.py @@ -61,7 +61,8 @@ class TuskarAPITests(test.APITestCase): def test_overcloud_stack(self): stack = self.heatclient_stacks.first() - oc = api.Overcloud(self.tuskarclient_overclouds.first(), request=None) + oc = api.Overcloud(self.tuskarclient_overclouds.first(), + request=object()) with patch('openstack_dashboard.api.heat.stack_get', return_value=stack): ret_val = oc.stack @@ -96,14 +97,16 @@ class TuskarAPITests(test.APITestCase): def test_overcloud_is_deployed(self): stack = self.heatclient_stacks.first() - oc = api.Overcloud(self.tuskarclient_overclouds.first(), request=None) + oc = api.Overcloud(self.tuskarclient_overclouds.first(), + request=object()) with patch('openstack_dashboard.api.heat.stack_get', return_value=stack): ret_val = oc.is_deployed self.assertFalse(ret_val) def test_overcloud_all_resources(self): - oc = api.Overcloud(self.tuskarclient_overclouds.first(), request=None) + oc = api.Overcloud(self.tuskarclient_overclouds.first(), + request=object()) # FIXME(lsmola) the stack call should not be tested in this unit test # anybody has idea how to do it? @@ -128,7 +131,8 @@ class TuskarAPITests(test.APITestCase): self.assertEqual(4, len(ret_val)) def test_overcloud_resources(self): - oc = api.Overcloud(self.tuskarclient_overclouds.first(), request=None) + oc = api.Overcloud(self.tuskarclient_overclouds.first(), + request=object()) role = api.OvercloudRole(self.tuskarclient_overcloud_roles.first()) # FIXME(lsmola) only all_resources and image_name should be tested @@ -152,15 +156,7 @@ class TuskarAPITests(test.APITestCase): ret_val = oc.resources(role) self.assertEqual(resource_list.call_count, 1) self.assertEqual(server_list.call_count, 1) - # TODO(lsmola) isn't it better to call image_list? - # this will call image_get for every unique image - # used that should not be much (4 images should be - # there for start) - # FIXME(lsmola) testing caching here is bad, - # because it gets cached for the whole tests run - self.assertEqual(image_get.call_count, 1) - # FIXME(lsmola) optimize this, it's enough to call - # node_list once + self.assertEqual(image_get.call_count, 2) self.assertEqual(node_list.call_count, 1) self.assertEqual(stack_get.call_count, 1) @@ -330,7 +326,7 @@ class TuskarAPITests(test.APITestCase): def test_resource_get(self): stack = self.heatclient_stacks.first() overcloud = api.Overcloud(self.tuskarclient_overclouds.first(), - request=None) + request=object()) resource = self.heatclient_resources.first() with patch('openstack_dashboard.api.heat.resource_get', @@ -351,7 +347,7 @@ class TuskarAPITests(test.APITestCase): with patch('novaclient.v1_1.contrib.baremetal.' 'BareMetalNodeManager.list', return_value=nodes): - ret_val = api.Resource(resource, request=None).node + ret_val = api.Resource(resource, request=object()).node self.assertIsInstance(ret_val, api.Node) self.assertIsInstance(ret_val.instance, servers.Server) diff --git a/tuskar_ui/test/test_data/tuskar_data.py b/tuskar_ui/test/test_data/tuskar_data.py index 1e715f20..ea24b4f2 100644 --- a/tuskar_ui/test/test_data/tuskar_data.py +++ b/tuskar_ui/test/test_data/tuskar_data.py @@ -421,8 +421,8 @@ def data(TEST): 'name': 'Block Storage Image'}) TEST.glanceclient_images.add(image_1, image_2, image_3, image_4) - # Nova flavors aka node profiles - # Do not include fields irrelevant for node profiles + # Nova flavors + # Do not include fields irrelevant for our usage TEST.novaclient_flavors = test_data_utils.TestDataContainer() flavor_1 = flavors.Flavor( flavors.FlavorManager(None), |