Initial to akanda-horizon repo
Change-Id: I3ee98229d2feea20abeb0fa996a9108bfba294a8
This commit is contained in:
parent
f6a61e8239
commit
2874515c22
|
@ -0,0 +1,12 @@
|
|||
.coverage
|
||||
*.py[co]
|
||||
*.sw[po]
|
||||
*.pyc
|
||||
*.egg-info
|
||||
.venv
|
||||
dropin.cache
|
||||
.tox
|
||||
build
|
||||
dist
|
||||
fpm.log*
|
||||
packages/
|
|
@ -0,0 +1,21 @@
|
|||
# Akanda Rug Horizon Extension
|
||||
|
||||
1. Install module
|
||||
|
||||
```
|
||||
pip install akanda-rug-horizon
|
||||
```
|
||||
|
||||
2. Copy extension files from the project root folder to ```/etc/openstack_dashboard/local/enabled``` or to ```/opt/stack/horizon/openstack_dashboard/local/enabled``` folder
|
||||
|
||||
```
|
||||
cp _80_admin_rug.py _81_admin_rug_rugrouters.py _82_admin_rug_rugtenants.py /opt/stack/horizon/openstack_dashboard/local/enabled/
|
||||
```
|
||||
|
||||
3. Specify rug management prefix, rug api port, and router image uuid in ```local_setting.py```
|
||||
|
||||
```
|
||||
RUG_MANAGEMENT_PREFIX = "fdca:3ba5:a17a:acda::/64"
|
||||
RUG_API_PORT = 44250
|
||||
ROUTER_IMAGE_UUID = "1e9c16f3-e070-47b7-b49c-ffcf38df5f9a"
|
||||
```
|
|
@ -0,0 +1,6 @@
|
|||
# The name of the panel group to be added to HORIZON_CONFIG. Required.
|
||||
PANEL_GROUP = 'rug'
|
||||
# The display name of the PANEL_GROUP. Required.
|
||||
PANEL_GROUP_NAME = 'RUG'
|
||||
# The name of the dashboard the PANEL_GROUP associated with. Required.
|
||||
PANEL_GROUP_DASHBOARD = 'admin'
|
|
@ -0,0 +1,10 @@
|
|||
# The name of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'rugrouters'
|
||||
# The name of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'admin'
|
||||
# The name of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'rug'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = \
|
||||
'rug_openstack_dashboard.dashboards.admin.rugrouters.panel.Rugrouters'
|
|
@ -0,0 +1,10 @@
|
|||
# The name of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'rugtenants'
|
||||
# The name of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'admin'
|
||||
# The name of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'rug'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = \
|
||||
'rug_openstack_dashboard.dashboards.admin.rugtenants.panel.Rugtenants'
|
|
@ -0,0 +1,139 @@
|
|||
from datetime import datetime
|
||||
from django.conf import settings
|
||||
from horizon.utils import functions as utils
|
||||
import netaddr
|
||||
import requests as r
|
||||
from openstack_dashboard.api.nova import novaclient
|
||||
from openstack_dashboard.api.neutron import neutronclient
|
||||
|
||||
|
||||
def _mgt_url(host, port, path):
|
||||
if ':' in host:
|
||||
host = '[%s]' % host
|
||||
return 'http://%s:%s%s' % (host, port, path)
|
||||
|
||||
|
||||
def _make_request(url):
|
||||
try:
|
||||
return r.put(url).ok
|
||||
except r.RequestException:
|
||||
return False
|
||||
|
||||
|
||||
def _get_local_service_ip(management_prefix):
|
||||
mgt_net = netaddr.IPNetwork(management_prefix)
|
||||
rug_ip = '%s/%s' % (netaddr.IPAddress(mgt_net.first + 1),
|
||||
mgt_net.prefixlen)
|
||||
return rug_ip
|
||||
|
||||
|
||||
class Router(object):
|
||||
id = ''
|
||||
name = ''
|
||||
status = ''
|
||||
latest = ''
|
||||
image_name = ''
|
||||
last_fetch = ''
|
||||
booted = ''
|
||||
|
||||
def __init__(self, **kw):
|
||||
for k, v in kw.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
|
||||
class RugClient(object):
|
||||
def __init__(self):
|
||||
self.host = (
|
||||
_get_local_service_ip(settings.RUG_MANAGEMENT_PREFIX)
|
||||
.split('/')[0]
|
||||
)
|
||||
self.port = settings.RUG_API_PORT
|
||||
self.image_uuid = settings.ROUTER_IMAGE_UUID
|
||||
self.api_limit = getattr(settings, 'API_RESULT_LIMIT', 1000)
|
||||
|
||||
def poll(self):
|
||||
path = '/poll'
|
||||
return _make_request(_mgt_url(self.host, self.port, path))
|
||||
|
||||
def config_reload(self):
|
||||
path = '/config/reload'
|
||||
return _make_request(_mgt_url(self.host, self.port, path))
|
||||
|
||||
def workers_debug(self):
|
||||
path = '/workers/debug'
|
||||
return _make_request(_mgt_url(self.host, self.port, path))
|
||||
|
||||
def router_debug(self, router_id):
|
||||
path = '/router/debug/{router_id}'.format(router_id=router_id)
|
||||
return _make_request(_mgt_url(self.host, self.port, path))
|
||||
|
||||
def router_manage(self, router_id):
|
||||
path = '/router/manage/{router_id}'.format(router_id=router_id)
|
||||
return _make_request(_mgt_url(self.host, self.port, path))
|
||||
|
||||
def router_update(self, router_id):
|
||||
path = '/router/update/{router_id}'.format(router_id=router_id)
|
||||
return _make_request(_mgt_url(self.host, self.port, path))
|
||||
|
||||
def router_rebuild(self, router_id, router_image_uuid=None):
|
||||
if router_image_uuid:
|
||||
path = ('/router/rebuild/{router_id}/--router_image_uuid/' +
|
||||
'{router_image_uuid}').format(
|
||||
router_id=router_id,
|
||||
router_image_uuid=router_image_uuid
|
||||
)
|
||||
else:
|
||||
path = '/router/rebuild/{router_id}/'.format(router_id=router_id)
|
||||
return _make_request(_mgt_url(self.host, self.port, path))
|
||||
|
||||
def tenant_debug(self, tenant_id):
|
||||
path = '/tenant/debug/{tenant_id}'.format(tenant_id=tenant_id)
|
||||
return _make_request(_mgt_url(self.host, self.port, path))
|
||||
|
||||
def tenant_manage(self, tenant_id):
|
||||
path = '/tenant/manage/{tenant_id}'.format(tenant_id=tenant_id)
|
||||
return _make_request(_mgt_url(self.host, self.port, path))
|
||||
|
||||
def get_routers(self, request, **search_opts):
|
||||
page_size = utils.get_page_size(request)
|
||||
paginate = False
|
||||
if 'paginate' in search_opts:
|
||||
paginate = search_opts.pop('paginate')
|
||||
search_opts['limit'] = page_size + 1
|
||||
if 'tenant_id' not in search_opts:
|
||||
search_opts['all_tenants'] = True
|
||||
|
||||
routers_metadata = []
|
||||
nova_client = novaclient(request)
|
||||
routers = (
|
||||
neutronclient(request)
|
||||
.list_routers(**search_opts)
|
||||
.get("routers", [])
|
||||
)
|
||||
for router in routers:
|
||||
search_opts = {'name': 'ak-' + router['id'], 'all_tenants': True}
|
||||
instances = nova_client.servers.list(True, search_opts=search_opts)
|
||||
instance = instances[0] if instances else None
|
||||
image = (
|
||||
nova_client.images.get(instance.image['id'])
|
||||
if instance else None
|
||||
)
|
||||
routers_metadata.append(Router(
|
||||
id=router['id'],
|
||||
name=router['name'],
|
||||
latest=image.id == self.image_uuid if image else '',
|
||||
image_name=image.name if image else '',
|
||||
last_fetch=datetime.utcnow(),
|
||||
booted=instance.created if instance else '',
|
||||
status=router['status'],
|
||||
tenant_id=router['tenant_id'],
|
||||
))
|
||||
|
||||
has_more_data = False
|
||||
if paginate and len(routers_metadata) > page_size:
|
||||
routers_metadata.pop(-1)
|
||||
has_more_data = True
|
||||
elif paginate and len(routers_metadata) == self.api_limit:
|
||||
has_more_data = True
|
||||
|
||||
return routers_metadata, has_more_data
|
|
@ -0,0 +1,68 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.template.defaultfilters import filesizeformat
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from openstack_dashboard.dashboards.project.images import utils
|
||||
|
||||
from rug_openstack_dashboard.api.rug import RugClient
|
||||
|
||||
|
||||
rc = RugClient()
|
||||
|
||||
|
||||
def _image_choice_title(img):
|
||||
gb = filesizeformat(img.size)
|
||||
return '%s (%s)' % (img.name or img.id, gb)
|
||||
|
||||
|
||||
class PollForm(forms.SelfHandlingForm):
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
rc.poll()
|
||||
messages.success(request, _('Routers were polled'))
|
||||
except Exception:
|
||||
exceptions.handle(request, _('Unable to poll routers.'))
|
||||
return True
|
||||
|
||||
|
||||
class RebuildForm(forms.SelfHandlingForm):
|
||||
router_id = forms.CharField(label=_("ID"),
|
||||
widget=forms.HiddenInput(),
|
||||
required=True)
|
||||
router_name = forms.CharField(label=_("Router Name"),
|
||||
widget=forms.HiddenInput(),
|
||||
required=False)
|
||||
attrs = {'class': 'image-selector'}
|
||||
image = forms.ChoiceField(label=_("Select Image"),
|
||||
widget=forms.SelectWidget(attrs=attrs,
|
||||
data_attrs=('size', 'display-name'),
|
||||
transform=_image_choice_title),
|
||||
required=False)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(RebuildForm, self).__init__(request, *args, **kwargs)
|
||||
images = utils.get_available_images(request, request.user.tenant_id)
|
||||
choices = [(image.id, image) for image in images]
|
||||
if choices:
|
||||
choices.insert(0, ("", _("Select Image")))
|
||||
else:
|
||||
choices.insert(0, ("", _("No images available")))
|
||||
self.fields['image'].choices = choices
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
if data['image']:
|
||||
rc.router_rebuild(data['router_id'], data['image'])
|
||||
else:
|
||||
rc.router_rebuild(data['router_id'])
|
||||
messages.success(request,
|
||||
_('Rebuilt Router: %s.') % data['router_name'])
|
||||
except Exception:
|
||||
exceptions.handle(
|
||||
request,
|
||||
_('Unable to rebuild router %s.' % data['router_name'])
|
||||
)
|
||||
return True
|
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
|
||||
"""
|
|
@ -0,0 +1,13 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.dashboards.admin import dashboard
|
||||
|
||||
|
||||
class Rugrouters(horizon.Panel):
|
||||
name = _("Routers")
|
||||
slug = "rugrouters"
|
||||
|
||||
|
||||
dashboard.Admin.register(Rugrouters)
|
|
@ -0,0 +1,137 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tables
|
||||
|
||||
from rug_openstack_dashboard.api.rug import RugClient
|
||||
|
||||
|
||||
rc = RugClient()
|
||||
|
||||
|
||||
class ManageAction(tables.BatchAction):
|
||||
name = "manage"
|
||||
|
||||
def get_default_classes(self):
|
||||
classes = super(tables.BatchAction, self).get_default_classes()
|
||||
classes += ("btn-danger", )
|
||||
return classes
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Manage Router",
|
||||
u"Manage Routers",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Managed Router",
|
||||
u"Managed Routers",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
try:
|
||||
rc.router_manage(obj_id)
|
||||
except Exception:
|
||||
msg = _('Failed to manage route %s') % obj_id
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
|
||||
class DebugAction(tables.BatchAction):
|
||||
name = "debug"
|
||||
|
||||
def get_default_classes(self):
|
||||
classes = super(tables.BatchAction, self).get_default_classes()
|
||||
classes += ("btn-danger", )
|
||||
return classes
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Debug Router",
|
||||
u"Debug Routers",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Debugged Router",
|
||||
u"Debugged Routers",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
try:
|
||||
rc.router_debug(obj_id)
|
||||
except Exception:
|
||||
msg = _('Failed to manage route %s') % obj_id
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
|
||||
class UpdateAction(tables.BatchAction):
|
||||
name = "update"
|
||||
|
||||
def get_default_classes(self):
|
||||
classes = super(tables.BatchAction, self).get_default_classes()
|
||||
classes += ("btn-danger", )
|
||||
return classes
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Update Router",
|
||||
u"Update Routers",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Updated Router",
|
||||
u"Updated Routers",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
try:
|
||||
rc.router_update(obj_id)
|
||||
except Exception:
|
||||
msg = _('Failed to manage route %s') % obj_id
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
|
||||
class PollAction(tables.LinkAction):
|
||||
name = "poll"
|
||||
verbose_name = _("Poll Routers")
|
||||
url = "horizon:admin:rugrouters:poll"
|
||||
classes = ("ajax-modal",)
|
||||
|
||||
|
||||
class RebuildAction(tables.LinkAction):
|
||||
name = "rebuild"
|
||||
verbose_name = _("Rebuild Router")
|
||||
url = "horizon:admin:rugrouters:rebuild"
|
||||
classes = ("ajax-modal",)
|
||||
|
||||
|
||||
class RouterTable(tables.DataTable):
|
||||
name = tables.Column("name", verbose_name=_("Name"),
|
||||
link="horizon:admin:routers:detail")
|
||||
status = tables.Column("status", verbose_name=_("Status"))
|
||||
latest = tables.Column('latest', verbose_name=_("Latest"))
|
||||
image_name = tables.Column('image_name', verbose_name=_("Image Name"))
|
||||
last_fetch = tables.Column('last_fetch', verbose_name=_("Last Fetch"))
|
||||
booted = tables.Column('booted', verbose_name=_("Booted"))
|
||||
|
||||
class Meta:
|
||||
name = "routers"
|
||||
verbose_name = _("Routers")
|
||||
table_actions = (ManageAction, DebugAction, PollAction)
|
||||
status_columns = ('status',)
|
||||
row_actions = (RebuildAction, UpdateAction, ManageAction, DebugAction,)
|
|
@ -0,0 +1,21 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}poll_rugrouters_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:admin:rugrouters:poll' %}{% endblock %}
|
||||
|
||||
{% block modal_id %}poll_rugrouters_modal{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Poll Routers" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="right">
|
||||
<p>{% trans "All routers will be polled" %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary" type="submit" value="{% trans "Poll Routers" %}" />
|
||||
<a href="{% url 'horizon:admin:rugrouters:index' %}" class="btn btn-default cancel">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
|
@ -0,0 +1,27 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}rebuild_rugrouters_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:admin:rugrouters:rebuild' router_id %}{% endblock %}
|
||||
|
||||
{% block modal_id %}rebuild_rugrouters_modal{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Rebuild Router" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description" %}:</h3>
|
||||
<p>{% trans "Choose image to rebuild router" %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary" type="submit" value="{% trans "Rebuild Router" %}" />
|
||||
<a href="{% url 'horizon:admin:rugrouters:index' %}" class="btn btn-default cancel">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Routers" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Routers") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ table.render }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Poll Routers" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Poll Routers") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include "admin/rugrouters/_poll.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Rebuild Router" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Rebuild Router") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include "admin/rugrouters/_rebuild.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,13 @@
|
|||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from rug_openstack_dashboard.dashboards.admin.rugrouters import views
|
||||
|
||||
ROUTERS = r'^(?P<router_id>[^/]+)/%s$'
|
||||
|
||||
urlpatterns = patterns(
|
||||
'rug_openstack_dashboard.dashboards.admin.rugrouters.views',
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^poll$', views.PollView.as_view(), name='poll'),
|
||||
url(ROUTERS % 'rebuild', views.RebuildView.as_view(), name='rebuild'),
|
||||
)
|
|
@ -0,0 +1,65 @@
|
|||
from django.core.urlresolvers import reverse
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import tables
|
||||
from horizon import forms
|
||||
from horizon import exceptions
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from rug_openstack_dashboard.dashboards.admin.rugrouters import \
|
||||
tables as router_tables
|
||||
from rug_openstack_dashboard.dashboards.admin.rugrouters import \
|
||||
forms as rugrouters_forms
|
||||
from rug_openstack_dashboard.api.rug import RugClient
|
||||
|
||||
|
||||
rc = RugClient()
|
||||
|
||||
|
||||
class IndexView(tables.DataTableView):
|
||||
table_class = router_tables.RouterTable
|
||||
template_name = 'admin/rugrouters/index.html'
|
||||
|
||||
def has_prev_data(self, table):
|
||||
return getattr(self, "_prev", False)
|
||||
|
||||
def has_more_data(self, table):
|
||||
return getattr(self, "_more", False)
|
||||
|
||||
def get_data(self):
|
||||
try:
|
||||
routers, self._more = rc.get_routers(self.request)
|
||||
return routers
|
||||
except Exception:
|
||||
url = reverse('horizon:admin:rugrouters:index')
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve routers\' details.'),
|
||||
redirect=url)
|
||||
|
||||
|
||||
class PollView(forms.ModalFormView):
|
||||
form_class = rugrouters_forms.PollForm
|
||||
template_name = 'admin/rugrouters/poll.html'
|
||||
success_url = reverse_lazy('horizon:admin:rugrouters:index')
|
||||
|
||||
|
||||
class RebuildView(forms.ModalFormView):
|
||||
form_class = rugrouters_forms.RebuildForm
|
||||
template_name = 'admin/rugrouters/rebuild.html'
|
||||
success_url = reverse_lazy('horizon:admin:rugrouters:index')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
self.router = api.neutron.router_get(self.request,
|
||||
self.kwargs['router_id'])
|
||||
context = super(RebuildView, self).get_context_data(**kwargs)
|
||||
context["router_id"] = self.kwargs['router_id']
|
||||
context["router_name"] = self.router['name']
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
return {
|
||||
'router_id': self.kwargs['router_id'],
|
||||
'router_name': self.get_context_data()['router_name']
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
"""
|
||||
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
|
||||
"""
|
|
@ -0,0 +1,13 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
|
||||
from openstack_dashboard.dashboards.admin import dashboard
|
||||
|
||||
|
||||
class Rugtenants(horizon.Panel):
|
||||
name = _("Tenants")
|
||||
slug = "rugtenants"
|
||||
|
||||
|
||||
dashboard.Admin.register(Rugtenants)
|
|
@ -0,0 +1,228 @@
|
|||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import tables
|
||||
from horizon import exceptions
|
||||
|
||||
from rug_openstack_dashboard.api.rug import RugClient
|
||||
|
||||
|
||||
rc = RugClient()
|
||||
|
||||
|
||||
class TenantFilterAction(tables.FilterAction):
|
||||
def filter(self, table, tenants, filter_string):
|
||||
q = filter_string.lower()
|
||||
|
||||
def comp(tenant):
|
||||
if q in tenant.name.lower():
|
||||
return True
|
||||
return False
|
||||
|
||||
return filter(comp, tenants)
|
||||
|
||||
|
||||
class TenantManageAction(tables.BatchAction):
|
||||
name = "manage"
|
||||
|
||||
def get_default_classes(self):
|
||||
classes = super(tables.BatchAction, self).get_default_classes()
|
||||
classes += ("btn-danger", )
|
||||
return classes
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Manage Tenant",
|
||||
u"Manage Tenants",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Managed Tenant",
|
||||
u"Managed Tenants",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
try:
|
||||
rc.tenant_manage(obj_id)
|
||||
except Exception:
|
||||
msg = _('Failed to manage route %s') % obj_id
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
|
||||
class TenantDebugAction(tables.BatchAction):
|
||||
name = "debug"
|
||||
|
||||
def get_default_classes(self):
|
||||
classes = super(tables.BatchAction, self).get_default_classes()
|
||||
classes += ("btn-danger", )
|
||||
return classes
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Debug Tenant",
|
||||
u"Debug Tenants",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Debugged Tenant",
|
||||
u"Debugged Tenants",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
try:
|
||||
rc.tenant_debug(obj_id)
|
||||
except Exception:
|
||||
msg = _('Failed to manage route %s') % obj_id
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
|
||||
class TenantsTable(tables.DataTable):
|
||||
name = tables.Column('name', verbose_name=_('Name'),
|
||||
link="horizon:admin:rugtenants:tenant")
|
||||
description = tables.Column(lambda obj: getattr(obj, 'description', None),
|
||||
verbose_name=_('Description'))
|
||||
id = tables.Column('id', verbose_name=_('Project ID'))
|
||||
enabled = tables.Column('enabled', verbose_name=_('Enabled'), status=True)
|
||||
|
||||
class Meta:
|
||||
name = "tenants"
|
||||
verbose_name = _("Tenants")
|
||||
row_actions = (TenantDebugAction, TenantManageAction, )
|
||||
table_actions = (TenantFilterAction, )
|
||||
pagination_param = "tenant_marker"
|
||||
|
||||
|
||||
class RouterManageAction(tables.BatchAction):
|
||||
name = "manage"
|
||||
|
||||
def get_default_classes(self):
|
||||
classes = super(tables.BatchAction, self).get_default_classes()
|
||||
classes += ("btn-danger", )
|
||||
return classes
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Manage Router",
|
||||
u"Manage Routers",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Managed Router",
|
||||
u"Managed Routers",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
try:
|
||||
rc.router_manage(obj_id)
|
||||
except Exception:
|
||||
msg = _('Failed to manage route %s') % obj_id
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
|
||||
class RouterDebugAction(tables.BatchAction):
|
||||
name = "debug"
|
||||
|
||||
def get_default_classes(self):
|
||||
classes = super(tables.BatchAction, self).get_default_classes()
|
||||
classes += ("btn-danger", )
|
||||
return classes
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Debug Router",
|
||||
u"Debug Routers",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Debugged Router",
|
||||
u"Debugged Routers",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
try:
|
||||
rc.router_debug(obj_id)
|
||||
except Exception:
|
||||
msg = _('Failed to manage route %s') % obj_id
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
|
||||
class RouterUpdateAction(tables.BatchAction):
|
||||
name = "update"
|
||||
|
||||
def get_default_classes(self):
|
||||
classes = super(tables.BatchAction, self).get_default_classes()
|
||||
classes += ("btn-danger", )
|
||||
return classes
|
||||
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Update Router",
|
||||
u"Update Routers",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Updated Router",
|
||||
u"Updated Routers",
|
||||
count
|
||||
)
|
||||
|
||||
def action(self, request, obj_id):
|
||||
try:
|
||||
rc.router_update(obj_id)
|
||||
except Exception:
|
||||
msg = _('Failed to manage route %s') % obj_id
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
|
||||
class RouterRebuildAction(tables.LinkAction):
|
||||
name = "rebuild"
|
||||
verbose_name = _("Rebuild Router")
|
||||
classes = ("ajax-modal",)
|
||||
|
||||
def get_link_url(self, datum=None):
|
||||
return reverse("horizon:admin:rugtenants:rebuild",
|
||||
kwargs={'tenant_id': datum.tenant_id,
|
||||
'router_id': datum.id})
|
||||
|
||||
|
||||
class TenantRouterTable(tables.DataTable):
|
||||
name = tables.Column("name", verbose_name=_("Name"),
|
||||
link="horizon:admin:routers:detail")
|
||||
status = tables.Column("status", verbose_name=_("Status"))
|
||||
latest = tables.Column('latest', verbose_name=_("Latest"))
|
||||
image_name = tables.Column('image_name', verbose_name=_("Image Name"))
|
||||
last_fetch = tables.Column('last_fetch', verbose_name=_("Last Fetch"))
|
||||
booted = tables.Column('booted', verbose_name=_("Booted"))
|
||||
|
||||
class Meta:
|
||||
name = "routers"
|
||||
verbose_name = _("Routers")
|
||||
table_actions = (RouterManageAction, RouterDebugAction, )
|
||||
status_columns = ('status',)
|
||||
row_actions = (RouterRebuildAction, RouterUpdateAction,
|
||||
RouterManageAction, RouterDebugAction, )
|
|
@ -0,0 +1,27 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}rebuild_rugtenants_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:admin:rugtenants:rebuild' router_id=router_id tenant_id=tenant_id %}{% endblock %}
|
||||
|
||||
{% block modal_id %}rebuild_rugtenants_modal{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Rebuild Router" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description" %}:</h3>
|
||||
<p>{% trans "Choose image to rebuild router" %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary" type="submit" value="{% trans "Rebuild Router" %}" />
|
||||
<a href="{% url 'horizon:admin:rugtenants:index' %}" class="btn btn-default cancel">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Tenants" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Tenants") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ table.render }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Rebuild Router" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Rebuild Router") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include "admin/rugtenants/_rebuild.html" %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Routers" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
{{ table.render }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,14 @@
|
|||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from rug_openstack_dashboard.dashboards.admin.rugtenants import views
|
||||
|
||||
TENANT = r'^(?P<tenant_id>[^/]+)/%s$'
|
||||
|
||||
urlpatterns = patterns(
|
||||
'rug_openstack_dashboard.dashboards.admin.rugtenants.views',
|
||||
url(r'^$', views.TenantIndexView.as_view(), name='index'),
|
||||
url(TENANT % '$', views.TenantRouterIndexView.as_view(), name='tenant'),
|
||||
url(r'^(?P<tenant_id>[^/]+)/(?P<router_id>[^/]+)/rebuild$',
|
||||
views.RebuildView.as_view(), name='rebuild'),
|
||||
)
|
|
@ -0,0 +1,123 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
|
||||
from horizon import tables
|
||||
from horizon import exceptions
|
||||
from horizon import messages
|
||||
from horizon import forms
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard import policy
|
||||
|
||||
from rug_openstack_dashboard.dashboards.admin.rugtenants import \
|
||||
tables as tenant_tables
|
||||
from rug_openstack_dashboard.dashboards.admin.rugrouters import \
|
||||
forms as rugrouters_forms
|
||||
from rug_openstack_dashboard.api.rug import RugClient
|
||||
|
||||
|
||||
rc = RugClient()
|
||||
|
||||
|
||||
class TenantIndexView(tables.DataTableView):
|
||||
table_class = tenant_tables.TenantsTable
|
||||
template_name = 'admin/rugtenants/index.html'
|
||||
|
||||
def has_more_data(self, table):
|
||||
return self._more
|
||||
|
||||
def get_data(self):
|
||||
tenants = []
|
||||
marker = self.request.GET.get(
|
||||
tenant_tables.TenantsTable._meta.pagination_param, None)
|
||||
domain_context = self.request.session.get('domain_context', None)
|
||||
if policy.check((("admin", "admin:list_projects"),), self.request):
|
||||
try:
|
||||
tenants, self._more = api.keystone.tenant_list(
|
||||
self.request,
|
||||
domain=domain_context,
|
||||
paginate=True,
|
||||
marker=marker)
|
||||
except Exception:
|
||||
self._more = False
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve project list."))
|
||||
elif policy.check((("admin", "identity:list_user_projects"),),
|
||||
self.request):
|
||||
try:
|
||||
tenants, self._more = api.keystone.tenant_list(
|
||||
self.request,
|
||||
user=self.request.user.id,
|
||||
paginate=True,
|
||||
marker=marker,
|
||||
admin=False)
|
||||
except Exception:
|
||||
self._more = False
|
||||
exceptions.handle(self.request,
|
||||
_("Unable to retrieve project information."))
|
||||
else:
|
||||
self._more = False
|
||||
msg = \
|
||||
_("Insufficient privilege level to view project information.")
|
||||
messages.info(self.request, msg)
|
||||
return tenants
|
||||
|
||||
|
||||
class TenantRouterIndexView(tables.DataTableView):
|
||||
table_class = tenant_tables.TenantRouterTable
|
||||
template_name = 'admin/rugtenants/router-index.html'
|
||||
|
||||
def has_prev_data(self, table):
|
||||
return getattr(self, "_prev", False)
|
||||
|
||||
def has_more_data(self, table):
|
||||
return getattr(self, "_more", False)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(TenantRouterIndexView, self).get_context_data(**kwargs)
|
||||
context["tenant_id"] = self.kwargs['tenant_id']
|
||||
tenant = api.keystone.tenant_get(self.request,
|
||||
self.kwargs['tenant_id'],
|
||||
admin=True)
|
||||
context["title"] = "Routers of tenant \"%s\"" % tenant.name
|
||||
return context
|
||||
|
||||
def get_data(self):
|
||||
try:
|
||||
routers, self._more = rc.get_routers(
|
||||
self.request,
|
||||
tenant_id=self.kwargs['tenant_id']
|
||||
)
|
||||
return routers
|
||||
except Exception:
|
||||
url = reverse('horizon:admin:rugtenants:index')
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve routers\' details.'),
|
||||
redirect=url)
|
||||
|
||||
|
||||
class RebuildView(forms.ModalFormView):
|
||||
form_class = rugrouters_forms.RebuildForm
|
||||
template_name = 'admin/rugtenants/rebuild.html'
|
||||
success_url = reverse_lazy('horizon:admin:rugtenants:index')
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("horizon:admin:rugtenants:tenant",
|
||||
args=(self.kwargs['tenant_id'],))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
self.router = api.neutron.router_get(self.request,
|
||||
self.kwargs['router_id'])
|
||||
context = super(RebuildView, self).get_context_data(**kwargs)
|
||||
context["router_id"] = self.kwargs['router_id']
|
||||
context["tenant_id"] = self.kwargs['tenant_id']
|
||||
context["router_name"] = self.router['name']
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
return {
|
||||
'router_id': self.kwargs['router_id'],
|
||||
'tenant_id': self.kwargs['tenant_id'],
|
||||
'router_name': self.get_context_data()['router_name']
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#!/usr/bin/env python
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name='akanda-rug-horizon',
|
||||
version='0.1',
|
||||
packages=find_packages("akanda"),
|
||||
package_dir={'': 'akanda'},
|
||||
install_requires=[
|
||||
'requests',
|
||||
],
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
unittest2
|
||||
nose
|
||||
coverage
|
||||
mock>=0.8.0
|
||||
pep8
|
||||
eventlet==0.12.1
|
||||
iso8601
|
||||
python-novaclient
|
||||
WebOb==1.2.3
|
||||
mox==0.5.3
|
||||
testtools
|
||||
fixtures
|
|
@ -0,0 +1,33 @@
|
|||
[tox]
|
||||
envlist = py27,pep8
|
||||
|
||||
[testenv]
|
||||
distribute = False
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = nosetests --with-coverage --cover-package=akanda.rug {posargs}
|
||||
sitepackages = False
|
||||
|
||||
[tox:jenkins]
|
||||
|
||||
[testenv:style]
|
||||
deps = flake8
|
||||
setuptools_git>=0.4
|
||||
commands = flake8 akanda setup.py
|
||||
|
||||
[testenv:pep8]
|
||||
deps = {[testenv:style]deps}
|
||||
commands = {[testenv:style]commands}
|
||||
|
||||
[testenv:doc]
|
||||
deps = Sphinx
|
||||
commands = sphinx-build doc/source doc/build
|
||||
|
||||
[testenv:cover]
|
||||
setenv = NOSE_WITH_COVERAGE=1
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[flake8]
|
||||
ignore = E123,E133,E226,E241,E242,E731
|
Loading…
Reference in New Issue