Removed "networks" panel from Horizon.

While this functionality will eventually be restored to Horizon,
when it does it will be in a significantly revamped and improved
form. For the Essex release, the amount of work required to get it
where it needs to be is too great and so it has regrettably been
cut from the current lineup of panels. :-(

Change-Id: I9c8b6e31303461a35270bea8d1ed30ad0ae6ed93
This commit is contained in:
Gabriel Hurley 2012-03-06 12:23:25 -08:00
parent ecbd98a0f4
commit 009a5de287
18 changed files with 0 additions and 1078 deletions

View File

@ -36,4 +36,3 @@ from horizon.api.glance import *
from horizon.api.keystone import *
from horizon.api.nova import *
from horizon.api.swift import *
from horizon.api.quantum import *

View File

@ -1,132 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
from __future__ import absolute_import
import logging
import urlparse
from quantum import client as quantum_client
from horizon.api.base import url_for
from horizon.api import nova
LOG = logging.getLogger(__name__)
# FIXME(gabriel): Add object wrappers for Quantum client. The quantum client
# returns plain dicts (a la Glance) which we should wrap.
def quantum_api(request):
tenant = request.user.tenant_id
url = urlparse.urlparse(url_for(request, 'network'))
return quantum_client.Client(url.hostname, url.port, False, tenant, 'json')
def quantum_list_networks(request):
return quantum_api(request).list_networks()
def quantum_network_details(request, network_id):
return quantum_api(request).show_network_details(network_id)
def quantum_list_ports(request, network_id):
return quantum_api(request).list_ports(network_id)
def quantum_port_details(request, network_id, port_id):
return quantum_api(request).show_port_details(network_id, port_id)
def quantum_create_network(request, data):
return quantum_api(request).create_network(data)
def quantum_delete_network(request, network_id):
return quantum_api(request).delete_network(network_id)
def quantum_update_network(request, network_id, data):
return quantum_api(request).update_network(network_id, data)
def quantum_create_port(request, network_id):
return quantum_api(request).create_port(network_id)
def quantum_delete_port(request, network_id, port_id):
return quantum_api(request).delete_port(network_id, port_id)
def quantum_attach_port(request, network_id, port_id, data):
return quantum_api(request).attach_resource(network_id, port_id, data)
def quantum_detach_port(request, network_id, port_id):
return quantum_api(request).detach_resource(network_id, port_id)
def quantum_set_port_state(request, network_id, port_id, data):
return quantum_api(request).update_port(network_id, port_id, data)
def quantum_port_attachment(request, network_id, port_id):
return quantum_api(request).show_port_attachment(network_id, port_id)
def get_vif_ids(request):
vifs = []
attached_vifs = []
# Get a list of all networks
networks_list = quantum_api(request).list_networks()
for network in networks_list['networks']:
ports = quantum_api(request).list_ports(network['id'])
# Get port attachments
for port in ports['ports']:
port_attachment = quantum_api(request).show_port_attachment(
network['id'],
port['id'])
if port_attachment['attachment']:
attached_vifs.append(
port_attachment['attachment']['id'].encode('ascii'))
# Get all instances
instances = nova.server_list(request)
# Get virtual interface ids by instance
for instance in instances:
id = instance.id
instance_vifs = nova.virtual_interfaces_list(request, id)
for vif in instance_vifs:
# Check if this VIF is already connected to any port
if str(vif.id) in attached_vifs:
vifs.append({
'id': vif.id,
'instance': instance.id,
'instance_name': instance.name,
'available': False})
else:
vifs.append({
'id': vif.id,
'instance': instance.id,
'instance_name': instance.name,
'available': True})
return vifs

View File

@ -26,7 +26,6 @@ class Nova(horizon.Dashboard):
'instances_and_volumes',
'access_and_security',
'images_and_snapshots'),
_("Network"): ('networks',),
_("Object Store"): ('containers',)}
default_panel = 'overview'
supports_tenants = True

View File

@ -1,131 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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
from django import shortcuts
from django.contrib import messages
from django.utils.translation import ugettext as _
from horizon import api
from horizon import exceptions
from horizon import forms
LOG = logging.getLogger(__name__)
class CreateNetwork(forms.SelfHandlingForm):
name = forms.CharField(required=True, label=_("Network Name"))
def handle(self, request, data):
network_name = data['name']
try:
data = {'network': {'name': network_name}}
api.quantum.quantum_create_network(request, data)
messages.success(request,
_('Network %s has been created.') % network_name)
except:
exceptions.handle(request, _("Unable to create network."))
return shortcuts.redirect('horizon:nova:networks:index')
class RenameNetwork(forms.SelfHandlingForm):
network = forms.CharField(widget=forms.HiddenInput())
new_name = forms.CharField(required=True)
def handle(self, request, data):
try:
LOG.info('Renaming network %s to %s' %
(data['network'], data['new_name']))
send_data = {'network': {'name': '%s' % data['new_name']}}
api.quantum_update_network(request, data['network'], send_data)
except Exception, e:
if not hasattr(e, 'message'):
e.message = str(e)
messages.error(request,
_('Unable to rename network %(network)s: %(msg)s') %
{"network": data['network'], "msg": e.message})
else:
msg = _('Network %(net)s has been renamed to %(new_name)s.') % {
"net": data['network'], "new_name": data['new_name']}
LOG.info(msg)
messages.success(request, msg)
return shortcuts.redirect('horizon:nova:networks:index')
class CreatePort(forms.SelfHandlingForm):
network = forms.CharField(widget=forms.HiddenInput())
ports_num = forms.IntegerField(required=True, label=_("Number of Ports"))
def handle(self, request, data):
try:
LOG.info('Creating %s ports on network %s' %
(data['ports_num'], data['network']))
for i in range(0, data['ports_num']):
api.quantum_create_port(request, data['network'])
except Exception, e:
if not hasattr(e, 'message'):
e.message = str(e)
messages.error(request,
_('Unable to create ports on network %(network)s: %(msg)s') %
{"network": data['network'], "msg": e.message})
else:
msg = _('%(num_ports)s ports created on network %(network)s.') % {
"num_ports": data['ports_num'], "network": data['network']}
LOG.info(msg)
messages.success(request, msg)
return shortcuts.redirect('horizon:nova:networks:detail',
network_id=data['network'])
class AttachPort(forms.SelfHandlingForm):
network = forms.CharField(widget=forms.HiddenInput())
port = forms.CharField(widget=forms.HiddenInput())
vif_id = forms.ChoiceField(label=_("Select VIF to connect"))
def __init__(self, request, *args, **kwargs):
super(AttachPort, self).__init__(*args, **kwargs)
# Populate VIF choices
vif_choices = [('', "Select a VIF")]
for vif in api.get_vif_ids(request):
if vif['available']:
name = "Instance %s VIF %s" % (vif['instance_name'], vif['id'])
vif_choices.append((vif['id'], name,))
self.fields['vif_id'].choices = vif_choices
@classmethod
def _instantiate(cls, request, *args, **kwargs):
return cls(request, *args, **kwargs)
def handle(self, request, data):
try:
body = {'attachment': {'id': '%s' % data['vif_id']}}
api.quantum_attach_port(request,
data['network'],
data['port'],
body)
messages.success(request, _("Port attached."))
except:
exceptions.handle(request, _('Unable to attach port.'))
return shortcuts.redirect("horizon:nova:networks:detail",
data['network'])

View File

@ -1,31 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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 horizon
from horizon.dashboards.nova import dashboard
class Networks(horizon.Panel):
name = "Networks"
slug = 'networks'
services = ("network",)
dashboard.Nova.register(Networks)

View File

@ -1,118 +0,0 @@
import logging
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import api
from horizon import tables
LOG = logging.getLogger(__name__)
class RenameNetworkLink(tables.LinkAction):
name = "rename_network"
verbose_name = _("Rename Network")
url = "horizon:nova:networks:rename"
attrs = {"class": "ajax-modal"}
class CreateNetworkLink(tables.LinkAction):
name = "create_network"
verbose_name = _("Create New Network")
url = "horizon:nova:networks:create"
classes = ("ajax-modal",)
class DeleteNetworkAction(tables.DeleteAction):
data_type_singular = _("Network")
data_type_plural = _("Networks")
def delete(self, request, obj_id):
api.quantum_delete_network(request, obj_id)
class NetworksTable(tables.DataTable):
id = tables.Column('id', verbose_name=_('Network Id'),
link="horizon:nova:networks:detail")
name = tables.Column('name', verbose_name=_('Name'))
used = tables.Column('used', verbose_name=_('Used'))
available = tables.Column('available', verbose_name=_('Available'))
total = tables.Column('total', verbose_name=_('Total'))
#tenant = tables.Column('tenant', verbose_name=_('Project'))
def get_object_id(self, datum):
return datum['id']
def get_object_display(self, obj):
return obj['name']
class Meta:
name = "networks"
verbose_name = _("Networks")
row_actions = (DeleteNetworkAction, RenameNetworkLink,)
table_actions = (CreateNetworkLink, DeleteNetworkAction,)
class CreatePortLink(tables.LinkAction):
name = "create_port"
verbose_name = _("Create Ports")
url = "horizon:nova:networks:port_create"
classes = ("ajax-modal",)
def get_link_url(self, datum=None):
network_id = self.table.kwargs['network_id']
return reverse(self.url, args=(network_id,))
class DeletePortAction(tables.DeleteAction):
data_type_singular = _("Port")
data_type_plural = _("Ports")
def delete(self, request, obj_id):
api.quantum_delete_port(request,
self.table.kwargs['network_id'],
obj_id)
class DetachPortAction(tables.BatchAction):
name = "detach_port"
action_present = _("Detach")
action_past = _("Detached")
data_type_singular = _("Port")
data_type_plural = _("Ports")
def action(self, request, datum_id):
body = {'port': {'state': 'DOWN'}}
api.quantum_set_port_state(request,
self.table.kwargs['network_id'],
datum_id, body)
class AttachPortAction(tables.LinkAction):
name = "attach_port"
verbose_name = _("Attach Port")
url = "horizon:nova:networks:port_attach"
attrs = {"class": "ajax-modal"}
def get_link_url(self, datum=None):
network_id = self.table.kwargs['network_id']
return reverse(self.url, args=(network_id, datum['id']))
class NetworkDetailsTable(tables.DataTable):
id = tables.Column('id', verbose_name=_('Port Id'))
state = tables.Column('state', verbose_name=_('State'))
attachment = tables.Column('attachment', verbose_name=_('Attachment'))
def get_object_id(self, datum):
return datum['id']
def get_object_display(self, obj):
return obj['id']
class Meta:
name = "network_details"
verbose_name = _("Network Port Details")
row_actions = (DeletePortAction, AttachPortAction, DetachPortAction)
table_actions = (CreatePortLink, DeletePortAction,)

View File

@ -1,295 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
from django import http
from django.core.urlresolvers import reverse
from mox import IsA
from horizon import api
from horizon import test
class NetworkViewTests(test.TestCase):
def setUp(self):
super(NetworkViewTests, self).setUp()
# TODO(gabriel): Move this to horizon.tests.test_data.quantum_data
# after the wrapper classes are added for Quantum.
self.network = {}
self.network['networks'] = []
self.network['networks'].append({'id': 'n1'})
self.network_details = {'network': {'id': '1', 'name': 'test_network'}}
self.ports = {}
self.ports['ports'] = []
self.ports['ports'].append({'id': 'p1'})
self.port_details = {
'port': {
'id': 'p1',
'state': 'DOWN'}}
self.port_attachment = {
'attachment': {
'id': 'vif1'}}
self.vifs = [{'id': 'vif1'}]
def test_network_index(self):
self.mox.StubOutWithMock(api, 'quantum_list_networks')
api.quantum_list_networks(IsA(http.HttpRequest)).\
AndReturn(self.network)
self.mox.StubOutWithMock(api, 'quantum_network_details')
api.quantum_network_details(IsA(http.HttpRequest),
'n1').AndReturn(self.network_details)
self.mox.StubOutWithMock(api, 'quantum_list_ports')
api.quantum_list_ports(IsA(http.HttpRequest),
'n1').AndReturn(self.ports)
self.mox.StubOutWithMock(api, 'quantum_port_attachment')
api.quantum_port_attachment(IsA(http.HttpRequest),
'n1', 'p1').AndReturn(self.port_attachment)
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:nova:networks:index'))
self.assertTemplateUsed(res, 'nova/networks/index.html')
networks = res.context['table'].data
self.assertEqual(len(networks), 1)
self.assertEqual(networks[0]['name'], 'test_network')
self.assertEqual(networks[0]['id'], 'n1')
self.assertEqual(networks[0]['total'], 1)
self.assertEqual(networks[0]['used'], 1)
self.assertEqual(networks[0]['available'], 0)
def test_network_create(self):
self.mox.StubOutWithMock(api.quantum, "quantum_create_network")
api.quantum.quantum_create_network(IsA(http.HttpRequest),
IsA(dict)).AndReturn(True)
self.mox.ReplayAll()
formData = {'name': 'Test',
'method': 'CreateNetwork'}
res = self.client.post(reverse('horizon:nova:networks:create'),
formData)
self.assertRedirectsNoFollow(res,
reverse('horizon:nova:networks:index'))
def test_network_delete(self):
self.mox.StubOutWithMock(api, "quantum_delete_network")
api.quantum_delete_network(IsA(http.HttpRequest), 'n1').AndReturn(True)
self.mox.StubOutWithMock(api, 'quantum_list_networks')
api.quantum_list_networks(IsA(http.HttpRequest)).\
AndReturn(self.network)
self.mox.StubOutWithMock(api, 'quantum_network_details')
api.quantum_network_details(IsA(http.HttpRequest),
'n1').AndReturn(self.network_details)
self.mox.StubOutWithMock(api, 'quantum_list_ports')
api.quantum_list_ports(IsA(http.HttpRequest),
'n1').AndReturn(self.ports)
self.mox.StubOutWithMock(api, 'quantum_port_attachment')
api.quantum_port_attachment(IsA(http.HttpRequest),
'n1', 'p1').AndReturn(self.port_attachment)
self.mox.ReplayAll()
formData = {'action': 'networks__delete__n1'}
self.client.post(reverse('horizon:nova:networks:index'), formData)
def test_network_rename(self):
self.mox.StubOutWithMock(api, 'quantum_network_details')
api.quantum_network_details(IsA(http.HttpRequest),
'n1').AndReturn(self.network_details)
self.mox.StubOutWithMock(api, 'quantum_update_network')
api.quantum_update_network(IsA(http.HttpRequest), 'n1',
{'network': {'name': "Test1"}})
self.mox.ReplayAll()
formData = {'network': 'n1',
'new_name': 'Test1',
'method': 'RenameNetwork'}
res = self.client.post(reverse('horizon:nova:networks:rename',
args=["n1"]),
formData)
self.assertRedirectsNoFollow(res,
reverse('horizon:nova:networks:index'))
def test_network_details(self):
self.mox.StubOutWithMock(api, 'quantum_network_details')
api.quantum_network_details(IsA(http.HttpRequest),
'n1').AndReturn(self.network_details)
self.mox.StubOutWithMock(api, 'quantum_list_ports')
api.quantum_list_ports(IsA(http.HttpRequest),
'n1').AndReturn(self.ports)
self.mox.StubOutWithMock(api, 'quantum_port_attachment')
api.quantum_port_attachment(IsA(http.HttpRequest),
'n1', 'p1').AndReturn(self.port_attachment)
self.mox.StubOutWithMock(api, 'quantum_port_details')
api.quantum_port_details(IsA(http.HttpRequest),
'n1', 'p1').AndReturn(self.port_details)
self.mox.StubOutWithMock(api, 'get_vif_ids')
api.get_vif_ids(IsA(http.HttpRequest)).AndReturn(self.vifs)
self.mox.ReplayAll()
res = self.client.get(reverse('horizon:nova:networks:detail',
args=['n1']))
self.assertTemplateUsed(res, 'nova/networks/detail.html')
self.assertIn('network', res.context)
network = res.context['network']
self.assertEqual(network['name'], 'test_network')
self.assertEqual(network['id'], 'n1')
def test_port_create(self):
self.mox.StubOutWithMock(api, "quantum_network_details")
self.mox.StubOutWithMock(api, "quantum_create_port")
network_details = {'network': {'id': 'n1'}}
api.quantum_network_details(IsA(http.HttpRequest),
'n1').AndReturn(network_details)
api.quantum_create_port(IsA(http.HttpRequest), 'n1').AndReturn(True)
formData = {'ports_num': 1,
'network': 'n1',
'method': 'CreatePort'}
self.mox.ReplayAll()
res = self.client.post(reverse('horizon:nova:networks:port_create',
args=["n1"]),
formData)
self.assertRedirectsNoFollow(res,
reverse('horizon:nova:networks:detail',
args=["n1"]))
def test_port_delete(self):
self.mox.StubOutWithMock(api, 'quantum_network_details')
self.mox.StubOutWithMock(api, 'quantum_list_ports')
self.mox.StubOutWithMock(api, 'quantum_port_attachment')
self.mox.StubOutWithMock(api, 'quantum_port_details')
self.mox.StubOutWithMock(api, 'get_vif_ids')
self.mox.StubOutWithMock(api, "quantum_delete_port")
network_details = {'network': {'id': 'n1', 'name': 'network1'}}
api.quantum_network_details(IsA(http.HttpRequest),
'n1').AndReturn(network_details)
api.quantum_list_ports(IsA(http.HttpRequest),
'n1').AndReturn(self.ports)
api.quantum_port_attachment(IsA(http.HttpRequest),
'n1', 'p1').AndReturn(self.port_attachment)
api.quantum_port_details(IsA(http.HttpRequest),
'n1', 'p1').AndReturn(self.port_details)
api.get_vif_ids(IsA(http.HttpRequest)).AndReturn(self.vifs)
api.quantum_delete_port(IsA(http.HttpRequest),
'n1', 'p1').AndReturn(True)
formData = {'action': 'network_details__delete__p1'}
self.mox.ReplayAll()
detail_url = reverse('horizon:nova:networks:detail', args=["n1"])
self.client.post(detail_url, formData)
def test_port_attach(self):
self.mox.StubOutWithMock(api, "quantum_network_details")
self.mox.StubOutWithMock(api, "quantum_attach_port")
self.mox.StubOutWithMock(api, "get_vif_ids")
network_details = {'network': {'id': 'n1'}}
api.quantum_network_details(IsA(http.HttpRequest),
'n1').AndReturn(network_details)
api.quantum_attach_port(IsA(http.HttpRequest),
'n1', 'p1', IsA(dict)).AndReturn(True)
api.get_vif_ids(IsA(http.HttpRequest)).AndReturn([{
'id': 'v1',
'instance_name': 'instance1',
'available': True}])
formData = {'port': 'p1',
'network': 'n1',
'vif_id': 'v1',
'method': 'AttachPort'}
self.mox.ReplayAll()
res = self.client.post(reverse('horizon:nova:networks:port_attach',
args=["n1", "p1"]),
formData)
self.assertRedirectsNoFollow(res,
reverse('horizon:nova:networks:detail',
args=["n1"]))
def test_port_detach(self):
self.mox.StubOutWithMock(api, 'quantum_network_details')
self.mox.StubOutWithMock(api, 'quantum_list_ports')
self.mox.StubOutWithMock(api, 'quantum_port_attachment')
self.mox.StubOutWithMock(api, 'quantum_port_details')
self.mox.StubOutWithMock(api, 'get_vif_ids')
self.mox.StubOutWithMock(api, "quantum_set_port_state")
network_details = {'network': {'id': 'n1', 'name': 'network1'}}
api.quantum_network_details(IsA(http.HttpRequest),
'n1').AndReturn(network_details)
api.quantum_list_ports(IsA(http.HttpRequest),
'n1').AndReturn(self.ports)
api.quantum_port_attachment(IsA(http.HttpRequest),
'n1', 'p1').AndReturn(self.port_attachment)
api.quantum_port_details(IsA(http.HttpRequest),
'n1', 'p1').AndReturn(self.port_details)
api.get_vif_ids(IsA(http.HttpRequest)).AndReturn(self.vifs)
api.quantum_set_port_state(IsA(http.HttpRequest),
'n1',
'p1',
{'port': {'state': 'DOWN'}}).AndReturn(True)
formData = {'action': "network_details__detach_port__p1"}
self.mox.ReplayAll()
detail_url = reverse('horizon:nova:networks:detail', args=["n1"])
res = self.client.post(detail_url, formData)
self.assertRedirectsNoFollow(res, detail_url)

View File

@ -1,36 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
from django.conf.urls.defaults import patterns, url
from .views import (IndexView, CreateView, RenameView,
DetailView, CreatePortView, AttachPortView)
urlpatterns = patterns('horizon.dashboards.nova.networks.views',
url(r'^$', IndexView.as_view(), name='index'),
url(r'^create/$', CreateView.as_view(), name='create'),
url(r'^(?P<network_id>[^/]+)/detail/$', DetailView.as_view(),
name='detail'),
url(r'^(?P<network_id>[^/]+)/rename/$', RenameView.as_view(),
name='rename'),
url(r'^(?P<network_id>[^/]+)/ports/create/$', CreatePortView.as_view(),
name='port_create'),
url(r'^(?P<network_id>[^/]+)/ports/(?P<port_id>[^/]+)/attach/$',
AttachPortView.as_view(), name='port_attach'))

View File

@ -1,212 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# 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.
"""
Views for managing Quantum networks.
"""
import logging
from django import http
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from horizon import api
from horizon import exceptions
from horizon import forms
from horizon import tables
from horizon.dashboards.nova.networks.forms import (CreateNetwork,
RenameNetwork, AttachPort, CreatePort)
from .tables import NetworksTable, NetworkDetailsTable
LOG = logging.getLogger(__name__)
class IndexView(tables.DataTableView):
table_class = NetworksTable
template_name = 'nova/networks/index.html'
def get_data(self):
tenant_id = self.request.user.tenant_id
networks = []
try:
networks_list = api.quantum_list_networks(self.request)
details = []
for network in networks_list['networks']:
net_stats = _calc_network_stats(self.request, network['id'])
# Get network details like name and id
details = api.quantum_network_details(self.request,
network['id'])
networks.append({
'name': details['network']['name'],
'id': network['id'],
'total': net_stats['total'],
'available': net_stats['available'],
'used': net_stats['used'],
'tenant': tenant_id})
except Exception, e:
LOG.exception("Unable to get network list.")
if not hasattr(e, 'message'):
e.message = str(e)
messages.error(self.request,
_('Unable to get network list: %s') % e.message)
return networks
class CreateView(forms.ModalFormView):
form_class = CreateNetwork
template_name = 'nova/networks/create.html'
class RenameView(forms.ModalFormView):
form_class = RenameNetwork
template_name = 'nova/networks/rename.html'
context_object_name = 'network'
def get_object(self, *args, **kwargs):
network_id = kwargs['network_id']
try:
return api.quantum_network_details(self.request,
network_id)['network']
except:
redirect = reverse("horizon:nova:networks:detail",
args=(network_id,))
exceptions.handle(self.request,
_('Unable to retrieve network information.'),
redirect=redirect)
def get_initial(self):
return {'network': self.object['id']}
class DetailView(tables.DataTableView):
table_class = NetworkDetailsTable
template_name = 'nova/networks/detail.html'
def get_data(self):
network_id = self.kwargs['network_id']
network_details = api.quantum_network_details(self.request, network_id)
self.network = {'id': network_id,
'name': network_details['network']['name'],
'ports': _get_port_states(self.request, network_id)}
return self.network['ports']
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context['network'] = self.network
return context
def _get_port_states(request, network_id):
"""
Helper method to find port states for a network
"""
network_ports = []
# Get all vifs for comparison with port attachments
vifs = api.get_vif_ids(request)
# Get all ports on this network
ports = api.quantum_list_ports(request, network_id)
for port in ports['ports']:
port_details = api.quantum_port_details(request,
network_id, port['id'])
# Get port attachments
port_attachment = api.quantum_port_attachment(request,
network_id, port['id'])
# Find instance the attachment belongs to
connected_instance = None
if port_attachment['attachment']:
for vif in vifs:
if str(vif['id']) == str(port_attachment['attachment']['id']):
connected_instance = vif['id']
break
network_ports.append({
'id': port_details['port']['id'],
'state': port_details['port']['state'],
'attachment': port_attachment['attachment'],
'instance': connected_instance})
return network_ports
def _calc_network_stats(request, network_id):
"""
Helper method to calculate statistics for a network
"""
# Get all ports statistics for the network
total = 0
available = 0
used = 0
ports = api.quantum_list_ports(request, network_id)
for port in ports['ports']:
total += 1
# Get port attachment
port_attachment = api.quantum_port_attachment(request,
network_id, port['id'])
if port_attachment['attachment']:
used += 1
else:
available += 1
return {'total': total, 'used': used, 'available': available}
class CreatePortView(forms.ModalFormView):
form_class = CreatePort
template_name = 'nova/networks/ports/create.html'
context_object_name = 'port'
def get_object(self, *args, **kwargs):
network_id = kwargs['network_id']
try:
return api.quantum_network_details(self.request,
network_id)['network']
except:
redirect = reverse("horizon:nova:networks:detail",
args=(network_id,))
exceptions.handle(self.request,
_('Unable to retrieve network information.'),
redirect=redirect)
def get_initial(self):
return {'network': self.object['id']}
class AttachPortView(forms.ModalFormView):
form_class = AttachPort
template_name = 'nova/networks/ports/attach.html'
context_object_name = 'network'
def get_object(self, *args, **kwargs):
network_id = kwargs['network_id']
try:
return api.quantum_network_details(self.request,
network_id)['network']
except:
redirect = reverse("horizon:nova:networks:detail",
args=(network_id,))
exceptions.handle(self.request,
_('Unable to attach port.'),
redirect=redirect)
def get_initial(self):
return {'network': self.object['id']}

View File

@ -1,24 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_id %}create_network_form{% endblock %}
{% block form_action %}{% url horizon:nova:networks:create %}{% endblock %}
{% block modal-header %}Create Network{% 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 "Networks provide layer 2 connectivity to your instances." %}</p>
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Network" %}" />
<a href="{% url horizon:nova:networks:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -1,24 +0,0 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block form_id %}form_rename_{{ network.id }}{% endblock %}
{% block form_action %}{% url horizon:nova:networks:rename network.id %}{% endblock %}
{% block modal-header %}Rename Network{% endblock %}
{% block modal-body %}
<div class="left">
<fieldset>
{% include "horizon/common/_form_fields.html" %}
</fieldset>
</div>
<div class="right">
<h3>{% trans "Rename" %}:</h3>
<p>{% trans "Enter a new name for your network." %}</p>
</div>
{% endblock %}
{% block modal-footer %}
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Rename Network" %}" />
<a href="{% url horizon:nova:networks:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
{% endblock %}

View File

@ -1,13 +0,0 @@
{% extends 'nova/base.html' %}
{% load i18n %}
{% block title %}Create Network{% endblock %}
{% block page_header %}
{# to make searchable false, just remove it from the include statement #}
{% include "horizon/common/_page_header.html" with title=_("Create Network") %}
{% endblock page_header %}
{% block dash_main %}
{% include 'nova/networks/_create.html' with form=network_form %}
{% endblock %}

View File

@ -1,11 +0,0 @@
{% extends 'nova/base.html' %}
{% load i18n %}
{% block title %}{% trans "Network Detail" %}{% endblock %}
{% block page_header %}
{% include "horizon/common/_page_header.html" with title=network.name %}
{% endblock page_header %}
{% block dash_main %}
{{ table.render }}
{% endblock %}

View File

@ -1,13 +0,0 @@
{% extends 'nova/base.html' %}
{% load i18n %}
{% block title %}Networks{% endblock %}
{% block page_header %}
{% url horizon:nova:networks:index as refresh_link %}
{# to make searchable false, just remove it from the include statement #}
{% include "horizon/common/_page_header.html" with title=_("Networks") refresh_link=refresh_link searchable="true" %}
{% endblock page_header %}
{% block dash_main %}
{{ table.render }}
{% endblock %}

View File

@ -1,13 +0,0 @@
{% extends 'nova/base.html' %}
{% load i18n %}
{% block title %}Rename Network{% endblock %}
{% block page_header %}
{# to make searchable false, just remove it from the include statement #}
{% include "horizon/common/_page_header.html" with title=_("Rename Network") %}
{% endblock page_header %}
{% block dash_main %}
{% include 'nova/networks/_rename.html' with form=rename_form %}
{% endblock %}

View File

@ -1,22 +0,0 @@
# 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.
from .utils import TestDataContainer
def data(TEST):
TEST.networks = TestDataContainer()
TEST.ports = TestDataContainer()
# TODO(gabriel): Move quantum test data into this module after it
# has been refactored with object wrappers (a la Glance).

View File

@ -20,5 +20,4 @@ iso8601
# Horizon Non-pip Requirements
-e git+https://github.com/openstack/python-novaclient.git#egg=python-novaclient
-e git+https://github.com/openstack/python-keystoneclient.git#egg=python-keystoneclient
-e git+https://github.com/openstack/python-quantumclient.git#egg=python-quantumclient
-e git+https://github.com/openstack/glance.git#egg=glance