diff --git a/Horizon/README.md b/Horizon/README.md index 29b7c6a..c6d2b1b 100644 --- a/Horizon/README.md +++ b/Horizon/README.md @@ -1,39 +1,6 @@ # OnRack plugin for OpenStack Horizon dashboard -On Shovel system: - -- Set monorail:httpHost, ironic:httpHost and keystone:httpHost found in ./shovel/config.json - -- Start shovel services: - - cd ./shovel ; nodejs index.js - -- On Horizon system: - - Set SHOVEL_URL in ./horizon-shovel/openstack_dashboard/dashboards/admin/hypervisors/baremetal/shovel.py - - Copy horizon-shovel contents to horizon: - - cp ./horizon-shovel/* /opt/stack/horizon - -- Restart Apache: - - sudo service apache2 restart - -- Enable OnRack event tasker: - - Install Celery: - - sudo pip install celery - - Start celery beat service: - - cd /opt/stack/horizon/ ; python manage.py celery worker -B -E - -- Connect to Horizon dashboard URL and login - -- Navigate to Admin -> System -> Hypervisors page - -- Click on 'Bare Metal' tab - - +git clone https://github.com/keedya/Shovel-horizon.git +cd Shovel-horizon/Horizon +sudo ./install.sh --url --location +sudo service apache2 restart diff --git a/Horizon/install.sh b/Horizon/install.sh new file mode 100755 index 0000000..0d17536 --- /dev/null +++ b/Horizon/install.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +TEMP=`getopt -o u:l: --long url: --long location: -- "$@"` + +if [ $? != 0 ] ; then echo "Exit" ; exit 1 ; fi + +eval set -- "$TEMP" +echo $TEMP +ADDR_IP=${ADDR_IP-} +FILE_LOC=${FILE_LOC-} + +while true ; do + case "$1" in + -u | --url) ADDR_IP=$2 ;shift 2 ;; + -l | --location) FILE_LOC=$2;shift 2 ;; + --) shift; break ;; + *) echo "Internal error!" ; exit 1 ;; + esac +done +echo "get shovel url: " $ADDR_IP +echo "get file location: " $FILE_LOC +if [ -z "$ADDR_IP" -o -z "$FILE_LOC" ] +then + echo "You must specify ipaddr of shovel and horizon location" + exit 1 +fi + +#replace in shovel.py SHOVEL_URL with the new addre value +sed -i "s|.*URI = .*|URI = \"$ADDR_IP\" + SHOVEL_BASE_API|g" rackhd/shovel.py +#copy rackhd to horizon admin dashboard +cp -r rackhd $FILE_LOC/openstack_dashboard/dashboards/admin +#copy _50_admin_rackhd_panels.py to dashboard enabled +cp _50_admin_rackhd_panels.py $FILE_LOC/openstack_dashboard/enabled diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/tabs.py b/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/tabs.py deleted file mode 100644 index 986e0cd..0000000 --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/tabs.py +++ /dev/null @@ -1,72 +0,0 @@ -# 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 -import json -import requests - -from django.utils.translation import ugettext_lazy as _ - -from horizon import exceptions -from horizon import tabs - -import openstack_dashboard.api -from openstack_dashboard.dashboards.admin.hypervisors.baremetal import tables -from openstack_dashboard.dashboards.admin.hypervisors.baremetal import shovel - -LOG = logging.getLogger(__name__) - -class OnRackTab(tabs.TableTab): - table_classes = (tables.BareMetalTable,) - name = _("Bare Metal") - slug = "baremetal" - template_name = "horizon/common/_data_table.html" - - class NodeData: - def __init__(self, uuid, name, hwaddr, events, state): - self.id = uuid - self.name = name - self.uuid = uuid - self.hwaddr = hwaddr - self.events = events - self.state = state - - def _find_ironic_node(self, id): - # ISSUE: iterating all nodes because query by name (onrack id) isn't working in ironic? - nodes = shovel.get_ironic_nodes() - for n in nodes['nodes']: - if n['extra'].get('nodeid', None) == id: - return n - return None - - def get_baremetal_data(self): - data = [] - try: - nodes = shovel.request_nodes_get() - for n in nodes: - if n['type'] in {'enclosure','switch'}: - continue - dmi = shovel.get_catalog_data_by_source(n['id'],'dmi') - name = dmi['System Information']['Product Name'] - hwaddr = n['name'] - id = n['id'] - events = '0' - n = self._find_ironic_node(id) - if n is not None: - events = n['extra'].get('eventcnt','0') - state = 'Registered' - else: - state = 'Unregistered' - data.append(self.NodeData(id, name, hwaddr, events, state)) - return data - except Exception, e: - LOG.error("Excepton in get_baremetal_data(): {0}".format(e)) - return data diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/tabs.py b/Horizon/openstack_dashboard/dashboards/admin/hypervisors/tabs.py deleted file mode 100644 index 918ac92..0000000 --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/tabs.py +++ /dev/null @@ -1,46 +0,0 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.utils.translation import ugettext_lazy as _ - -from horizon import exceptions -from horizon import tabs - -from openstack_dashboard.api import nova -from openstack_dashboard.dashboards.admin.hypervisors.compute \ - import tabs as cmp_tabs -from openstack_dashboard.dashboards.admin.hypervisors.baremetal \ - import tabs as baremetal_tabs -from openstack_dashboard.dashboards.admin.hypervisors import tables - - -class HypervisorTab(tabs.TableTab): - table_classes = (tables.AdminHypervisorsTable,) - name = _("Hypervisor") - slug = "hypervisor" - template_name = "horizon/common/_detail_table.html" - - def get_hypervisors_data(self): - hypervisors = [] - try: - hypervisors = nova.hypervisor_list(self.request) - except Exception: - exceptions.handle(self.request, - _('Unable to retrieve hypervisor information.')) - - return hypervisors - - -class HypervisorHostTabs(tabs.TabGroup): - slug = "hypervisor_info" - tabs = (HypervisorTab, cmp_tabs.ComputeHostTab, baremetal_tabs.OnRackTab) - sticky = True diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/urls.py b/Horizon/openstack_dashboard/dashboards/admin/hypervisors/urls.py deleted file mode 100644 index 72aee70..0000000 --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/urls.py +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2013 B1 Systems GmbH -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from django.conf.urls import include -from django.conf.urls import patterns -from django.conf.urls import url - -from openstack_dashboard.dashboards.admin.hypervisors.compute \ - import urls as compute_urls -from openstack_dashboard.dashboards.admin.hypervisors.baremetal \ - import urls as baremetal_urls - -from openstack_dashboard.dashboards.admin.hypervisors import views - - -urlpatterns = patterns( - 'openstack_dashboard.dashboards.admin.hypervisors.views', - url(r'^(?P[^/]+)/$', - views.AdminDetailView.as_view(), - name='detail'), - url(r'^$', views.AdminIndexView.as_view(), name='index'), - url(r'', include(compute_urls, namespace='compute')), - url(r'', include(baremetal_urls, namespace='baremetal')), -) diff --git a/Horizon/openstack_dashboard/dashboards/admin/tasks.py b/Horizon/openstack_dashboard/dashboards/admin/tasks.py deleted file mode 100644 index 3dc513c..0000000 --- a/Horizon/openstack_dashboard/dashboards/admin/tasks.py +++ /dev/null @@ -1,91 +0,0 @@ -from django.utils.translation import ugettext_lazy as _ -from openstack_dashboard.dashboards.admin.hypervisors.baremetal import shovel -from openstack_dashboard.api import nova -from horizon import exceptions -from horizon import messages - -from celery.decorators import task -from datetime import timedelta - -import re -import logging -import json - -LOG = logging.getLogger(__name__) - -def log_sel_all(sel): - """ Simple SEL logger """ - for entry in sel: - LOG.info( - 'logId: {0}\n' - 'sensorType: {1}\n' - 'sensorNumber: {2}\n' - 'event: {3}\n' - 'asserted: {4}\n' - .format(entry['logId'], - entry['sensorType'], - entry['sensorNumber'], - entry['event'], - entry['value'])) - return True - - -def log_sel_entry(entry): - """ Simple SEL entry logger """ - LOG.info( - 'logId: {0}\n' - 'sensorType: {1}\n' - 'sensorNumber: {2}\n' - 'event: {3}\n' - 'asserted: {4}\n' - .format(entry['logId'], - entry['sensorType'], - entry['sensorNumber'], - entry['event'], - entry['value'])) - return True - - -def find_sel_entry_re(sel, regex): - """ Return a list of searched regex expressions in each SEL entry """ - entry_list = [] - for entry in sel: - if regex and regex.strip(): - match = re.search(r""+regex+"", json.dumps(entry, ensure_ascii=True)) - if match: - entry_list.append(entry) - return entry_list - - -def update_events(events, ecount, entry_list, uuid): - """ Update the ironic nodes extra metadata with new event match """ - for entry in entry_list: - # update the latest event - if int(entry['time']) > int(events['time']): - LOG.info('update_event(): updating event for node {0} time:{1}'.format(uuid,entry['time'])) - ecount += 1 - p = json.loads( '[ {"path": "/extra/events", "value": ' + json.dumps(entry, ensure_ascii=True) + ', "op": "replace"}, {"path": "/extra/eventcnt", "value": ' + str(ecount) + ', "op": "replace"} ]' ) - shovel.node_patch(uuid, p) - return True - - -@task() -def SELPoller(): - """ Periodic task to poll for monorail SEL events """ - nodes = shovel.get_ironic_nodes() - for n in nodes['nodes']: - extra = n['extra'] - nodeid = extra.get('nodeid', None) - name = extra.get('name', None) - events = extra.get('events', None) - ecount = int(extra.get('eventcnt', 0)) - if nodeid is not None: - failnode = extra.get('failover', None) - regex = extra.get('eventre', None) - if regex is not None: - sel = shovel.get_current_sel_data(nodeid)[0].get('sel', None) - if sel is not None: - update_events(events, ecount, find_sel_entry_re(sel, regex), n['uuid']) - return True - - \ No newline at end of file diff --git a/Horizon/openstack_dashboard/settings.py b/Horizon/openstack_dashboard/settings.py deleted file mode 100644 index f13a706..0000000 --- a/Horizon/openstack_dashboard/settings.py +++ /dev/null @@ -1,355 +0,0 @@ -# Copyright 2012 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# Copyright 2012 Nebula, Inc. -# -# 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 -import os -import sys -import warnings - -import django -from django.utils.translation import ugettext_lazy as _ - -from openstack_dashboard import exceptions -from openstack_dashboard.static_settings import get_staticfiles_dirs # noqa - -import djcelery -from datetime import timedelta - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': '/opt/stack/horizon/openstack_dashboard/static/db/horizon.db', - } -} - -CELERYBEAT_SCHEDULE = { - "runs-every-5-seconds": { - "task": "openstack_dashboard.dashboards.admin.tasks.SELPoller", - "schedule": timedelta(seconds=5) - }, -} -CELERY_ALWAYS_EAGER = False -CELERYBEAT_SCHEDULER = "djcelery.schedulers.DatabaseScheduler" -djcelery.setup_loader() - -warnings.formatwarning = lambda message, category, *args, **kwargs: \ - '%s: %s' % (category.__name__, message) - -ROOT_PATH = os.path.dirname(os.path.abspath(__file__)) -BIN_DIR = os.path.abspath(os.path.join(ROOT_PATH, '..', 'bin')) - -if ROOT_PATH not in sys.path: - sys.path.append(ROOT_PATH) - -DEBUG = False -TEMPLATE_DEBUG = DEBUG - -SITE_BRANDING = 'OpenStack Dashboard' - -WEBROOT = '/' -LOGIN_URL = None -LOGOUT_URL = None -LOGIN_REDIRECT_URL = None - - -ROOT_URLCONF = 'openstack_dashboard.urls' - -HORIZON_CONFIG = { - 'user_home': 'openstack_dashboard.views.get_user_home', - 'ajax_queue_limit': 10, - 'auto_fade_alerts': { - 'delay': 3000, - 'fade_duration': 1500, - 'types': ['alert-success', 'alert-info'] - }, - 'help_url': "http://docs.openstack.org", - 'exceptions': {'recoverable': exceptions.RECOVERABLE, - 'not_found': exceptions.NOT_FOUND, - 'unauthorized': exceptions.UNAUTHORIZED}, - 'angular_modules': [], - 'js_files': [], - 'js_spec_files': [], -} - -# Set to True to allow users to upload images to glance via Horizon server. -# When enabled, a file form field will appear on the create image form. -# See documentation for deployment considerations. -HORIZON_IMAGES_ALLOW_UPLOAD = True - -# The OPENSTACK_IMAGE_BACKEND settings can be used to customize features -# in the OpenStack Dashboard related to the Image service, such as the list -# of supported image formats. -OPENSTACK_IMAGE_BACKEND = { - 'image_formats': [ - ('', _('Select format')), - ('aki', _('AKI - Amazon Kernel Image')), - ('ami', _('AMI - Amazon Machine Image')), - ('ari', _('ARI - Amazon Ramdisk Image')), - ('docker', _('Docker')), - ('iso', _('ISO - Optical Disk Image')), - ('ova', _('OVA - Open Virtual Appliance')), - ('qcow2', _('QCOW2 - QEMU Emulator')), - ('raw', _('Raw')), - ('vdi', _('VDI - Virtual Disk Image')), - ('vhd', _('VHD - Virtual Hard Disk')), - ('vmdk', _('VMDK - Virtual Machine Disk')), - ] -} - -MIDDLEWARE_CLASSES = ( - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', -) -if django.VERSION >= (1, 8, 0): - MIDDLEWARE_CLASSES += ( - 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',) -else: - MIDDLEWARE_CLASSES += ('django.middleware.doc.XViewMiddleware',) -MIDDLEWARE_CLASSES += ( - 'horizon.middleware.HorizonMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', -) - -TEMPLATE_CONTEXT_PROCESSORS = ( - 'django.core.context_processors.debug', - 'django.core.context_processors.i18n', - 'django.core.context_processors.request', - 'django.core.context_processors.media', - 'django.core.context_processors.static', - 'django.contrib.messages.context_processors.messages', - 'horizon.context_processors.horizon', - 'openstack_dashboard.context_processors.openstack', -) - -TEMPLATE_LOADERS = ( - 'django.template.loaders.filesystem.Loader', - 'django.template.loaders.app_directories.Loader', - 'horizon.loaders.TemplateLoader', -) - -TEMPLATE_DIRS = ( - os.path.join(ROOT_PATH, 'templates'), -) - -STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - 'compressor.finders.CompressorFinder', -) - -COMPRESS_PRECOMPILERS = ( - ('text/scss', 'django_pyscss.compressor.DjangoScssFilter'), -) - -COMPRESS_CSS_FILTERS = ( - 'compressor.filters.css_default.CssAbsoluteFilter', -) - -COMPRESS_ENABLED = True -COMPRESS_OUTPUT_DIR = 'dashboard' -COMPRESS_CSS_HASHING_METHOD = 'hash' -COMPRESS_PARSER = 'compressor.parser.HtmlParser' - -INSTALLED_APPS = [ - 'openstack_dashboard', - 'django.contrib.contenttypes', - 'django.contrib.auth', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.contrib.humanize', - 'django_pyscss', - 'openstack_dashboard.django_pyscss_fix', - 'compressor', - 'horizon', - 'openstack_auth', - 'periodically', - 'djcelery', -] - -TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' -AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',) -AUTHENTICATION_URLS = ['openstack_auth.urls'] -MESSAGE_STORAGE = 'django.contrib.messages.storage.fallback.FallbackStorage' - -SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' -SESSION_COOKIE_HTTPONLY = True -SESSION_EXPIRE_AT_BROWSER_CLOSE = True -SESSION_COOKIE_SECURE = False -SESSION_TIMEOUT = 1800 -# A token can be near the end of validity when a page starts loading, and -# invalid during the rendering which can cause errors when a page load. -# TOKEN_TIMEOUT_MARGIN defines a time in seconds we retrieve from token -# validity to avoid this issue. You can adjust this time depending on the -# performance of the infrastructure. -TOKEN_TIMEOUT_MARGIN = 10 - -# When using cookie-based sessions, log error when the session cookie exceeds -# the following size (common browsers drop cookies above a certain size): -SESSION_COOKIE_MAX_SIZE = 4093 - -# when doing upgrades, it may be wise to stick to PickleSerializer -# NOTE(berendt): Check during the K-cycle if this variable can be removed. -# https://bugs.launchpad.net/horizon/+bug/1349463 -SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' - -LANGUAGES = ( - ('de', 'German'), - ('en', 'English'), - ('en-au', 'Australian English'), - ('en-gb', 'British English'), - ('es', 'Spanish'), - ('fr', 'French'), - ('hi', 'Hindi'), - ('ja', 'Japanese'), - ('ko', 'Korean (Korea)'), - ('nl', 'Dutch (Netherlands)'), - ('pl', 'Polish'), - ('pt-br', 'Portuguese (Brazil)'), - ('ru', 'Russian'), - ('sr', 'Serbian'), - ('zh-cn', 'Simplified Chinese'), - ('zh-tw', 'Chinese (Taiwan)'), -) -LANGUAGE_CODE = 'en' -LANGUAGE_COOKIE_NAME = 'horizon_language' -USE_I18N = True -USE_L10N = True -USE_TZ = True - -OPENSTACK_KEYSTONE_DEFAULT_ROLE = '_member_' - -DEFAULT_EXCEPTION_REPORTER_FILTER = 'horizon.exceptions.HorizonReporterFilter' - -POLICY_FILES_PATH = os.path.join(ROOT_PATH, "conf") -# Map of local copy of service policy files -POLICY_FILES = { - 'identity': 'keystone_policy.json', - 'compute': 'nova_policy.json', - 'volume': 'cinder_policy.json', - 'image': 'glance_policy.json', - 'orchestration': 'heat_policy.json', - 'network': 'neutron_policy.json', - 'telemetry': 'ceilometer_policy.json', -} - -SECRET_KEY = None -LOCAL_PATH = None - -SECURITY_GROUP_RULES = { - 'all_tcp': { - 'name': _('All TCP'), - 'ip_protocol': 'tcp', - 'from_port': '1', - 'to_port': '65535', - }, - 'all_udp': { - 'name': _('All UDP'), - 'ip_protocol': 'udp', - 'from_port': '1', - 'to_port': '65535', - }, - 'all_icmp': { - 'name': _('All ICMP'), - 'ip_protocol': 'icmp', - 'from_port': '-1', - 'to_port': '-1', - }, -} - -ADD_INSTALLED_APPS = [] - -# STATIC directory for custom theme, set as default. -# It can be overridden in local_settings.py -CUSTOM_THEME_PATH = 'static/themes/default' - -try: - from local.local_settings import * # noqa -except ImportError: - logging.warning("No local_settings file found.") - -if not WEBROOT.endswith('/'): - WEBROOT += '/' -if LOGIN_URL is None: - LOGIN_URL = WEBROOT + 'auth/login/' -if LOGOUT_URL is None: - LOGOUT_URL = WEBROOT + 'auth/logout/' -if LOGIN_REDIRECT_URL is None: - LOGIN_REDIRECT_URL = WEBROOT - -MEDIA_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'media')) -MEDIA_URL = WEBROOT + 'media/' -STATIC_ROOT = os.path.abspath(os.path.join(ROOT_PATH, '..', 'static')) -STATIC_URL = WEBROOT + 'static/' -STATICFILES_DIRS = get_staticfiles_dirs(WEBROOT) - -CUSTOM_THEME = os.path.join(ROOT_PATH, CUSTOM_THEME_PATH) -STATICFILES_DIRS.append( - ('custom', CUSTOM_THEME), -) - -# Load the pluggable dashboard settings -import openstack_dashboard.enabled -import openstack_dashboard.local.enabled -from openstack_dashboard.utils import settings - -INSTALLED_APPS = list(INSTALLED_APPS) # Make sure it's mutable -settings.update_dashboards( - [ - openstack_dashboard.enabled, - openstack_dashboard.local.enabled, - ], - HORIZON_CONFIG, - INSTALLED_APPS, -) -INSTALLED_APPS[0:0] = ADD_INSTALLED_APPS - -# Ensure that we always have a SECRET_KEY set, even when no local_settings.py -# file is present. See local_settings.py.example for full documentation on the -# horizon.utils.secret_key module and its use. -if not SECRET_KEY: - if not LOCAL_PATH: - LOCAL_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), - 'local') - - from horizon.utils import secret_key - SECRET_KEY = secret_key.generate_or_read_from_file(os.path.join(LOCAL_PATH, - '.secret_key_store')) - -from openstack_dashboard import policy_backend -POLICY_CHECK_FUNCTION = policy_backend.check - -# Add HORIZON_CONFIG to the context information for offline compression -COMPRESS_OFFLINE_CONTEXT = { - 'STATIC_URL': STATIC_URL, - 'HORIZON_CONFIG': HORIZON_CONFIG, -} - -if DEBUG: - logging.basicConfig(level=logging.DEBUG) - -# during django reloads and an active user is logged in, the monkey -# patch below will not otherwise be applied in time - resulting in developers -# appearing to be logged out. In typical production deployments this section -# below may be omitted, though it should not be harmful -from openstack_auth import utils as auth_utils -auth_utils.patch_middleware_get_user() diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/__init__.py b/Horizon/rackhd/__init__.py old mode 100644 new mode 100755 similarity index 100% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/__init__.py rename to Horizon/rackhd/__init__.py diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/forms.py b/Horizon/rackhd/forms.py old mode 100644 new mode 100755 similarity index 96% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/forms.py rename to Horizon/rackhd/forms.py index 62cc5af..c768d62 --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/forms.py +++ b/Horizon/rackhd/forms.py @@ -20,7 +20,7 @@ from horizon import forms from horizon import messages from openstack_dashboard import api -from openstack_dashboard.dashboards.admin.hypervisors.baremetal import shovel +from openstack_dashboard.dashboards.admin.rackhd import shovel LOG = logging.getLogger(__name__) @@ -30,7 +30,7 @@ class RegisterForm(forms.SelfHandlingForm): driver = forms.ChoiceField(label=_('Driver'), required=True, widget=forms.Select(attrs={'class': 'switchable','data-slug': 'driver'})) kernel = forms.ChoiceField(label=_('Deploy Kernel'), required=True, - widget=forms.Select(attrs={'class': 'switchable'})) + widget=forms.Select(attrs={'class': 'switchable'})) ramdisk = forms.ChoiceField(label=_('Deploy RAM Disk'), required=True, widget=forms.Select(attrs={'class': 'switchable'})) port = forms.ChoiceField(label=_('Mac address'), required=True, @@ -50,11 +50,11 @@ class RegisterForm(forms.SelfHandlingForm): widget=forms.PasswordInput(attrs={'class': 'switched','data-switch-on': 'driver','data-driver-pxe_ssh': _('SSH Password')})) sshport = forms.CharField(required=False, widget=forms.TextInput(attrs={'class': 'switched','data-switch-on': 'driver','data-driver-pxe_ssh': _('SSH Port')})) - - failovernode = forms.ChoiceField(label=_("Failover Node"), required=False) - enfailover = forms.BooleanField(label=_("Enable Failover"), initial=False, required=False) - eventre = forms.CharField(max_length=255, label=_('Event Trigger (regex)'), required=False, initial='') - + + failovernode = forms.ChoiceField(label=_("Failover Node"), required=False) + enfailover = forms.BooleanField(label=_("Enable Failover"), initial=False, required=False) + eventre = forms.CharField(max_length=255, label=_('Event Trigger (regex)'), required=False, initial='') + def __init__(self, request, *args, **kwargs): super(RegisterForm, self).__init__(request, *args, **kwargs) self._node = kwargs['initial'].get('node', None) @@ -65,7 +65,7 @@ class RegisterForm(forms.SelfHandlingForm): self.fields['name'].initial = shovel.get_catalog_data_by_source(self._node['id'],'dmi')['System Information']['Product Name'] self.fields['uuid'].initial = self._node['id'] self.fields['driver'].choices = [ (elem,_(elem)) for elem in self._drivers ] - self.fields['kernel'].choices = [ (elem,_(elem)) for elem in self._ramdisk ] + self.fields['kernel'].choices = [ (elem,_(elem)) for elem in self._ramdisk ] self.fields['ramdisk'].choices = [ (elem,_(elem)) for elem in self._ramdisk ] self.fields['port'].choices = [ (elem,_(elem)) for elem in self._macaddress] # BMC information initials @@ -84,7 +84,7 @@ class RegisterForm(forms.SelfHandlingForm): nodes = shovel.request_nodes_get() self.fields['failovernode'].choices = [ (n['id'],_(n['id'])) for n in nodes if n['id'] != self._node['id'] ] else: - redirect = reverse('horizon:admin:hypervisors:index') + redirect = reverse('horizon:admin:rackhd:index') msg = 'Invalid node ID specified' messages.error(request, _(msg)) raise ValueError(msg) @@ -107,7 +107,7 @@ class RegisterForm(forms.SelfHandlingForm): messages.success(request, msg) return True except Exception: - redirect = reverse('horizon:admin:hypervisors:index') + redirect = reverse('horizon:admin:rackhd:index') msg = _('Failed to register baremetal node: {0} ({1})'.format(data['uuid'], data['name'])) messages.error(request, msg) return False @@ -141,7 +141,7 @@ class UnregisterForm(forms.SelfHandlingForm): else: raise Exception(result) except Exception: - redirect = reverse('horizon:admin:hypervisors:index') + redirect = reverse('horizon:admin:rackhd:index') msg = _('Failed to unregister baremetal node: {0}'.format(data['uuid'])) messages.error(request, msg) return False diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/json2html.py b/Horizon/rackhd/json2html.py old mode 100644 new mode 100755 similarity index 100% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/json2html.py rename to Horizon/rackhd/json2html.py diff --git a/Horizon/rackhd/panel.py b/Horizon/rackhd/panel.py new file mode 100755 index 0000000..6ff4dc7 --- /dev/null +++ b/Horizon/rackhd/panel.py @@ -0,0 +1,19 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from django.utils.translation import ugettext_lazy as _ + +import horizon +class Rackhd(horizon.Panel): + name = _("RackHD") + slug = "rackhd" + permissions = ('openstack.roles.admin',) \ No newline at end of file diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/shovel.py b/Horizon/rackhd/shovel.py old mode 100644 new mode 100755 similarity index 96% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/shovel.py rename to Horizon/rackhd/shovel.py index 14eb63c..0f33de4 --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/shovel.py +++ b/Horizon/rackhd/shovel.py @@ -20,9 +20,8 @@ from horizon import exceptions LOG = logging.getLogger(__name__) -SHOVEL_URL = 'http://10.240.19.192:9005' SHOVEL_BASE_API = '/api/1.1' -URI = SHOVEL_URL + SHOVEL_BASE_API +URI = "http://10.240.19.171:9005" + SHOVEL_BASE_API def get_driver_list(): r = requests.get(URI + '/ironic/drivers') diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/tables.py b/Horizon/rackhd/tables.py old mode 100644 new mode 100755 similarity index 91% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/tables.py rename to Horizon/rackhd/tables.py index 8f726a1..fd416b0 --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/tables.py +++ b/Horizon/rackhd/tables.py @@ -27,7 +27,7 @@ class RegisterSelectedNodes(tables.LinkAction): verbose_name = _("Register Selected") icon = "plus" classes = ("ajax-modal",) - url = "horizon:admin:hypervisors:baremetal:register" + url = "horizon:admin:rackhd:register" def get_link_url(self, datum=None, *args, **kwargs): return reverse(self.url) @@ -37,7 +37,7 @@ class UnregisterSelectedNodes(tables.LinkAction): verbose_name = _("Unegister Selected") icon = "minus" classes = ("ajax-modal",) - url = "horizon:admin:hypervisors:baremetal:unregister" + url = "horizon:admin:rackhd:unregister" def get_link_url(self, datum=None, *args, **kwargs): return reverse(self.url) @@ -47,7 +47,7 @@ class RegisterNode(tables.LinkAction): verbose_name = _("Register") icon = "plus" classes = ("ajax-modal",) - url = "horizon:admin:hypervisors:baremetal:register" + url = "horizon:admin:rackhd:register" class Failover(tables.LinkAction): @@ -55,7 +55,7 @@ class Failover(tables.LinkAction): verbose_name = _("Failover") icon = "minus" classes = ("ajax-modal",) - url = "horizon:admin:hypervisors:baremetal:failover" + url = "horizon:admin:rackhd:failover" class UnregisterNode(tables.LinkAction): @@ -63,7 +63,7 @@ class UnregisterNode(tables.LinkAction): verbose_name = _("Unregister") icon = "minus" classes = ("ajax-modal",) - url = "horizon:admin:hypervisors:baremetal:unregister" + url = "horizon:admin:rackhd:unregister" class BareMetalFilterAction(tables.FilterAction): @@ -113,14 +113,15 @@ class BareMetalAllEventsTable(tables.DataTable): class BareMetalTable(tables.DataTable): - name = tables.Column('name', verbose_name=_('Name'), link="horizon:admin:hypervisors:baremetal:detail", ) + name = tables.Column('name', verbose_name=_('Name'), link="horizon:admin:rackhd:detail", ) uuid = tables.Column('uuid', verbose_name=_('Node ID') ) hwaddr = tables.Column('hwaddr', verbose_name=_('MAC Address') ) - events = tables.Column('events', verbose_name=_('Events'), link="horizon:admin:hypervisors:baremetal:events" ) + events = tables.Column('events', verbose_name=_('Events'), link="horizon:admin:rackhd:events" ) state = tables.Column('state', verbose_name=_('State')) class Meta(object): name = "baremetal" - verbose_name = _("RackHD") + verbose_name = _("Baremetal Compute Nodes") table_actions = (BareMetalFilterAction,) multi_select = False row_actions = (RegisterNode, UnregisterNode,) + diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/_register.html b/Horizon/rackhd/templates/rackhd/_register.html old mode 100644 new mode 100755 similarity index 87% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/_register.html rename to Horizon/rackhd/templates/rackhd/_register.html index 72a95a3..813308e --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/_register.html +++ b/Horizon/rackhd/templates/rackhd/_register.html @@ -3,7 +3,7 @@ {% load url from future %} {% block form_id %}register_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:hypervisors:baremetal:register' baremetal %}{% endblock %} +{% block form_action %}{% url 'horizon:admin:rackhd:register' baremetal %}{% endblock %} {% block modal-header %}{% trans "Register Node" %}{% endblock %} diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/_unregister.html b/Horizon/rackhd/templates/rackhd/_unregister.html old mode 100644 new mode 100755 similarity index 87% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/_unregister.html rename to Horizon/rackhd/templates/rackhd/_unregister.html index d38fda0..3574f6a --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/_unregister.html +++ b/Horizon/rackhd/templates/rackhd/_unregister.html @@ -3,7 +3,7 @@ {% load url from future %} {% block form_id %}unregister_form{% endblock %} -{% block form_action %}{% url 'horizon:admin:hypervisors:baremetal:unregister' baremetal %}{% endblock %} +{% block form_action %}{% url 'horizon:admin:rackhd:unregister' baremetal %}{% endblock %} {% block modal-header %}{% trans "Unregister Node" %}{% endblock %} diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/detail.html b/Horizon/rackhd/templates/rackhd/detail.html old mode 100644 new mode 100755 similarity index 100% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/detail.html rename to Horizon/rackhd/templates/rackhd/detail.html diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/events.html b/Horizon/rackhd/templates/rackhd/events.html old mode 100644 new mode 100755 similarity index 98% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/events.html rename to Horizon/rackhd/templates/rackhd/events.html index e41df86..92431fa --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/events.html +++ b/Horizon/rackhd/templates/rackhd/events.html @@ -3,10 +3,10 @@ {% block title %}{% trans "Node Events" %}{% endblock %} {% block main %} -
- {{ lastevent_table.render }} -
-
- {{ allevents_table.render }} +
+ {{ lastevent_table.render }} +
+
+ {{ allevents_table.render }}
{% endblock %} diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/index.html b/Horizon/rackhd/templates/rackhd/index.html old mode 100644 new mode 100755 similarity index 100% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/index.html rename to Horizon/rackhd/templates/rackhd/index.html diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/register.html b/Horizon/rackhd/templates/rackhd/register.html old mode 100644 new mode 100755 similarity index 67% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/register.html rename to Horizon/rackhd/templates/rackhd/register.html index 42f0931..e68ca42 --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/register.html +++ b/Horizon/rackhd/templates/rackhd/register.html @@ -3,5 +3,5 @@ {% block title %}{% trans "Register" %}{% endblock %} {% block main %} - {% include 'admin/hypervisors/baremetal/_register.html' %} + {% include 'admin/rackhd/_register.html' %} {% endblock %} diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/unregister.html b/Horizon/rackhd/templates/rackhd/unregister.html old mode 100644 new mode 100755 similarity index 67% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/unregister.html rename to Horizon/rackhd/templates/rackhd/unregister.html index cc456b5..5dbd53e --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/templates/hypervisors/baremetal/unregister.html +++ b/Horizon/rackhd/templates/rackhd/unregister.html @@ -3,5 +3,5 @@ {% block title %}{% trans "Unregister" %}{% endblock %} {% block main %} - {% include 'admin/hypervisors/baremetal/_unregister.html' %} + {% include 'admin/rackhd/_unregister.html' %} {% endblock %} diff --git a/Horizon/rackhd/tests.py b/Horizon/rackhd/tests.py new file mode 100755 index 0000000..b9d7be7 --- /dev/null +++ b/Horizon/rackhd/tests.py @@ -0,0 +1,19 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from horizon.test import helpers as test + + +class RackhdTests(test.TestCase): + # Unit tests for rackhd. + def test_me(self): + self.assertTrue(1 + 1 == 2) diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/urls.py b/Horizon/rackhd/urls.py old mode 100644 new mode 100755 similarity index 80% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/urls.py rename to Horizon/rackhd/urls.py index 6631b86..990ece9 --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/urls.py +++ b/Horizon/rackhd/urls.py @@ -2,7 +2,7 @@ # 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 +# 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 @@ -12,14 +12,17 @@ from django.conf.urls import patterns from django.conf.urls import url +from django.conf.urls import include + +from openstack_dashboard.dashboards.admin.rackhd import views -from openstack_dashboard.dashboards.admin.hypervisors.baremetal import views urlpatterns = patterns( - 'openstack_dashboard.dashboards.admin.hypervisors.baremetal.views', + 'openstack_dashboard.dashboards.admin.rackhd.views', + url(r'^$', views.IndexView.as_view(), name='index'), url(r'^(?P[^/]+)/register$', views.RegisterView.as_view(), name='register'), url(r'^(?P[^/]+)/unregister$', views.UnregisterView.as_view(), name='unregister'), url(r'^(?P[^/]+)/detail$', views.BareMetalDetailView.as_view(), name='detail'), url(r'^(?P[^/]+)/events$', views.BareMetalEventView.as_view(), name='events'), url(r'^(?P[^/]+)/failover$', views.FailoverView.as_view(), name='failover'), -) +) \ No newline at end of file diff --git a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/views.py b/Horizon/rackhd/views.py old mode 100644 new mode 100755 similarity index 75% rename from Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/views.py rename to Horizon/rackhd/views.py index 8313482..9f83b5a --- a/Horizon/openstack_dashboard/dashboards/admin/hypervisors/baremetal/views.py +++ b/Horizon/rackhd/views.py @@ -2,14 +2,13 @@ # 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 +# 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 import json import pprint @@ -24,23 +23,71 @@ from horizon import tables from horizon import messages from openstack_dashboard import api -from openstack_dashboard.dashboards.admin.hypervisors.baremetal \ + +from openstack_dashboard.dashboards.admin.rackhd \ import forms as baremetal_forms -from openstack_dashboard.dashboards.admin.hypervisors.baremetal \ +from openstack_dashboard.dashboards.admin.rackhd \ import tables as baremetal_tables -from openstack_dashboard.dashboards.admin.hypervisors.baremetal import shovel -from openstack_dashboard.dashboards.admin.hypervisors.baremetal \ +from openstack_dashboard.dashboards.admin.rackhd \ import json2html as j2h -import json +from openstack_dashboard.dashboards.admin.rackhd import shovel + LOG = logging.getLogger(__name__) +class IndexView(tables.DataTableView): + # A very simple class-based view... + table_class = baremetal_tables.BareMetalTable + template_name = "admin/rackhd/index.html" + page_title = _("Baremetal") + + class NodeData: + def __init__(self, uuid, name, hwaddr, events, state): + self.id = uuid + self.name = name + self.uuid = uuid + self.hwaddr = hwaddr + self.events = events + self.state = state + + def get_data(self): + data = [] + try: + nodes = shovel.request_nodes_get() + i = 0 + for n in nodes: + dmi = shovel.get_catalog_data_by_source(n['id'],'dmi') + name = dmi['System Information']['Product Name'] + hwaddr = n['name'] + id = n['id'] + events = '0' + n = self._find_ironic_node(id) + if n is not None: + events = n['extra'].get('eventcnt','0') + state = 'Registered' + else: + state = 'Unregistered' + i += i +1 + data.append(self.NodeData(id, name, hwaddr, events, state)) + return data + except Exception, e: + print + LOG.error("Excepton in get_baremetal_data(): {0}".format(e)) + return data + def _find_ironic_node(self, id): + # ISSUE: iterating all nodes because query by name (onrack id) isn't working in ironic? + nodes = shovel.get_ironic_nodes() + for n in nodes['nodes']: + if n['extra'].get('nodeid', None) == id: + return n + return None + class RegisterView(forms.ModalFormView): context_object_name = 'baremetal' - template_name = 'admin/hypervisors/baremetal/register.html' + template_name = 'admin/rackhd/register.html' form_class = baremetal_forms.RegisterForm - success_url = reverse_lazy('horizon:admin:hypervisors:index') + success_url = reverse_lazy('horizon:admin:rackhd:index') page_title = _("Register Node") def get_context_data(self, **kwargs): @@ -64,9 +111,9 @@ class RegisterView(forms.ModalFormView): class UnregisterView(forms.ModalFormView): context_object_name = 'baremetal' - template_name = 'admin/hypervisors/baremetal/unregister.html' + template_name = 'admin/rackhd/unregister.html' form_class = baremetal_forms.UnregisterForm - success_url = reverse_lazy('horizon:admin:hypervisors:index') + success_url = reverse_lazy('horizon:admin:rackhd:index') page_title = _("Unegister Node") def get_context_data(self, **kwargs): @@ -84,9 +131,9 @@ class UnregisterView(forms.ModalFormView): class FailoverView(forms.ModalFormView): context_object_name = 'baremetal' - template_name = 'admin/hypervisors/baremetal/register.html' + template_name = 'admin/rackhd/register.html' form_class = baremetal_forms.RegisterForm - success_url = reverse_lazy('horizon:admin:hypervisors:index') + success_url = reverse_lazy('horizon:admin:rackhd:index') page_title = _("Failover") def _find_ironic_node(self, id): @@ -100,7 +147,7 @@ class FailoverView(forms.ModalFormView): result = shovel.unregister_node_del(id) return True except Exception: - redirect = reverse('horizon:admin:hypervisors:index') + redirect = reverse('horizon:admin:rackhd:index') return False def get_context_data(self, **kwargs): @@ -125,7 +172,7 @@ class FailoverView(forms.ModalFormView): else: raise ValueError('Registered node not found') except ValueError as e: - redirect = reverse('horizon:admin:hypervisors:index') + redirect = reverse('horizon:admin:rackhd:index') messages.error(self.request, _(e.message)) raise Exception(e.message) self._remove_node(current_id) @@ -135,7 +182,7 @@ class FailoverView(forms.ModalFormView): class BareMetalDetailView(tables.DataTableView): table_class = baremetal_tables.BareMetalDetailsTable - template_name = 'admin/hypervisors/baremetal/detail.html' + template_name = 'admin/rackhd/detail.html' page_title = _('Details') class CatalogData: @@ -158,7 +205,7 @@ class BareMetalDetailView(tables.DataTableView): class BareMetalEventView(tables.MultiTableView): table_classes = (baremetal_tables.BareMetalLastEventTable, baremetal_tables.BareMetalAllEventsTable,) - template_name = 'admin/hypervisors/baremetal/events.html' + template_name = 'admin/rackhd/events.html' page_title = _('Events') name = _("Events") slug = "events" @@ -208,7 +255,7 @@ class BareMetalEventView(tables.MultiTableView): try: sel = shovel.get_current_sel_data(id)[0].get('sel', []) except KeyError as e: - redirect = reverse('horizon:admin:hypervisors:index') + redirect = reverse('horizon:admin:rackhd:index') messages.error(self.request, _('No SEL data available, check node {0} poller task'.format(id))) raise KeyError(e.message) data = []