Initial to akanda-horizon repo

Change-Id: I3ee98229d2feea20abeb0fa996a9108bfba294a8
This commit is contained in:
Dmytro Vasylchenko 2015-05-14 14:27:02 +03:00
parent f6a61e8239
commit 2874515c22
37 changed files with 1094 additions and 0 deletions

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
.coverage
*.py[co]
*.sw[po]
*.pyc
*.egg-info
.venv
dropin.cache
.tox
build
dist
fpm.log*
packages/

21
README.md Normal file
View File

@ -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"
```

6
_80_admin_rug.py Normal file
View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,3 @@
"""
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
"""

View File

@ -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)

View File

@ -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,)

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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'),
)

View File

@ -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']
}

View File

@ -0,0 +1,3 @@
"""
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
"""

View File

@ -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)

View File

@ -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, )

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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'),
)

View File

@ -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']
}

12
setup.py Normal file
View File

@ -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',
],
)

12
test-requirements.txt Normal file
View File

@ -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

33
tox.ini Normal file
View File

@ -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