summaryrefslogtreecommitdiff
path: root/openstack_dashboard/dashboards/admin/networks
diff options
context:
space:
mode:
authorGabriel Hurley <gabriel@strikeawe.com>2012-10-04 15:43:40 -0700
committerGabriel Hurley <gabriel@strikeawe.com>2012-10-11 11:47:50 -0700
commitcb8e7c1f8f0b238b88253cd6d82092cbe530ba9e (patch)
tree9ee3463e05ae6cf2f9cee5309a648538471c02b9 /openstack_dashboard/dashboards/admin/networks
parentef1e1d9b7a1fd4140518318ead8b7174a6a434ab (diff)
downloadhorizon-cb8e7c1f8f0b238b88253cd6d82092cbe530ba9e.tar.gz
Splits OpenStack Dashboard bits from framework app code.
Moves everything OpenStack-specific (dashboards, apis, etc.) into the openstack_dashboard project, achieving a much cleaner separation between the project-specific code and the generic Horizon framework code. Change-Id: I7235b41d449b26c980668fc3eb4360b24508717b
Diffstat (limited to 'openstack_dashboard/dashboards/admin/networks')
-rw-r--r--openstack_dashboard/dashboards/admin/networks/__init__.py0
-rw-r--r--openstack_dashboard/dashboards/admin/networks/forms.py90
-rw-r--r--openstack_dashboard/dashboards/admin/networks/panel.py29
-rw-r--r--openstack_dashboard/dashboards/admin/networks/ports/__init__.py0
-rw-r--r--openstack_dashboard/dashboards/admin/networks/ports/forms.py93
-rw-r--r--openstack_dashboard/dashboards/admin/networks/ports/tables.py85
-rw-r--r--openstack_dashboard/dashboards/admin/networks/ports/tabs.py49
-rw-r--r--openstack_dashboard/dashboards/admin/networks/ports/urls.py28
-rw-r--r--openstack_dashboard/dashboards/admin/networks/ports/views.py99
-rw-r--r--openstack_dashboard/dashboards/admin/networks/subnets/__init__.py0
-rw-r--r--openstack_dashboard/dashboards/admin/networks/subnets/forms.py53
-rw-r--r--openstack_dashboard/dashboards/admin/networks/subnets/tables.py83
-rw-r--r--openstack_dashboard/dashboards/admin/networks/subnets/urls.py29
-rw-r--r--openstack_dashboard/dashboards/admin/networks/subnets/views.py103
-rw-r--r--openstack_dashboard/dashboards/admin/networks/tables.py82
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/_create.html25
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/_update.html24
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/create.html11
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/index.html21
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_create.html25
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_update.html29
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/ports/create.html11
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/ports/update.html11
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/_create.html25
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/_update.html33
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/create.html11
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/index.html11
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/update.html11
-rw-r--r--openstack_dashboard/dashboards/admin/networks/templates/networks/update.html11
-rw-r--r--openstack_dashboard/dashboards/admin/networks/tests.py805
-rw-r--r--openstack_dashboard/dashboards/admin/networks/urls.py47
-rw-r--r--openstack_dashboard/dashboards/admin/networks/views.py140
32 files changed, 2074 insertions, 0 deletions
diff --git a/openstack_dashboard/dashboards/admin/networks/__init__.py b/openstack_dashboard/dashboards/admin/networks/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/__init__.py
diff --git a/openstack_dashboard/dashboards/admin/networks/forms.py b/openstack_dashboard/dashboards/admin/networks/forms.py
new file mode 100644
index 000000000..5a969bfec
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/forms.py
@@ -0,0 +1,90 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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.core.urlresolvers import reverse
+from django.utils.translation import ugettext_lazy as _
+
+from horizon import exceptions
+from horizon import forms
+from horizon import messages
+
+from openstack_dashboard import api
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CreateNetwork(forms.SelfHandlingForm):
+ name = forms.CharField(max_length=255,
+ label=_("Name"),
+ required=False)
+ tenant_id = forms.ChoiceField(label=_("Project"))
+ shared = forms.BooleanField(label=_("Shared"),
+ initial=False, required=False)
+
+ @classmethod
+ def _instantiate(cls, request, *args, **kwargs):
+ return cls(request, *args, **kwargs)
+
+ def __init__(self, request, *args, **kwargs):
+ super(CreateNetwork, self).__init__(request, *args, **kwargs)
+ tenant_choices = [('', _("Select a project"))]
+ for tenant in api.keystone.tenant_list(request, admin=True):
+ if tenant.enabled:
+ tenant_choices.append((tenant.id, tenant.name))
+ self.fields['tenant_id'].choices = tenant_choices
+
+ def handle(self, request, data):
+ try:
+ network = api.quantum.network_create(request,
+ name=data['name'],
+ tenant_id=data['tenant_id'],
+ shared=data['shared'])
+ msg = _('Network %s was successfully created.') % data['name']
+ LOG.debug(msg)
+ messages.success(request, msg)
+ return network
+ except:
+ redirect = reverse('horizon:admin:networks:index')
+ msg = _('Failed to create network %s') % data['name']
+ exceptions.handle(request, msg, redirect=redirect)
+
+
+class UpdateNetwork(forms.SelfHandlingForm):
+ name = forms.CharField(label=_("Name"), required=False)
+ tenant_id = forms.CharField(widget=forms.HiddenInput)
+ network_id = forms.CharField(label=_("ID"),
+ widget=forms.TextInput(
+ attrs={'readonly': 'readonly'}))
+ shared = forms.BooleanField(label=_("Shared"), required=False)
+ failure_url = 'horizon:admin:networks:index'
+
+ def handle(self, request, data):
+ try:
+ network = api.quantum.network_modify(request, data['network_id'],
+ name=data['name'],
+ shared=data['shared'])
+ msg = _('Network %s was successfully updated.') % data['name']
+ LOG.debug(msg)
+ messages.success(request, msg)
+ return network
+ except:
+ msg = _('Failed to update network %s') % data['name']
+ LOG.info(msg)
+ redirect = reverse(self.failure_url)
+ exceptions.handle(request, msg, redirect=redirect)
diff --git a/openstack_dashboard/dashboards/admin/networks/panel.py b/openstack_dashboard/dashboards/admin/networks/panel.py
new file mode 100644
index 000000000..22774c6c6
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/panel.py
@@ -0,0 +1,29 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from django.utils.translation import ugettext_lazy as _
+
+import horizon
+
+from openstack_dashboard.dashboards.admin import dashboard
+
+
+class Networks(horizon.Panel):
+ name = _("Networks")
+ slug = 'networks'
+ permissions = ('openstack.services.network',)
+
+dashboard.Admin.register(Networks)
diff --git a/openstack_dashboard/dashboards/admin/networks/ports/__init__.py b/openstack_dashboard/dashboards/admin/networks/ports/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/ports/__init__.py
diff --git a/openstack_dashboard/dashboards/admin/networks/ports/forms.py b/openstack_dashboard/dashboards/admin/networks/ports/forms.py
new file mode 100644
index 000000000..19d0838cd
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/ports/forms.py
@@ -0,0 +1,93 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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.core.urlresolvers import reverse
+from django.utils.translation import ugettext_lazy as _
+
+from horizon import exceptions
+from horizon import forms
+from horizon import messages
+
+from openstack_dashboard import api
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CreatePort(forms.SelfHandlingForm):
+ network_name = forms.CharField(label=_("Network Name"),
+ widget=forms.TextInput(
+ attrs={'readonly': 'readonly'}))
+ network_id = forms.CharField(label=_("Network ID"),
+ widget=forms.TextInput(
+ attrs={'readonly': 'readonly'}))
+ name = forms.CharField(max_length=255,
+ label=_("Name"),
+ required=False)
+ device_id = forms.CharField(max_length=100, label=_("Device ID"),
+ help_text='Device ID attached to the port',
+ required=False)
+
+ def handle(self, request, data):
+ try:
+ # We must specify tenant_id of the network which a subnet is
+ # created for if admin user does not belong to the tenant.
+ network = api.quantum.network_get(request, data['network_id'])
+ data['tenant_id'] = network.tenant_id
+
+ port = api.quantum.port_create(request, **data)
+ msg = _('Port %s was successfully created.') % port['id']
+ LOG.debug(msg)
+ messages.success(request, msg)
+ return port
+ except:
+ msg = _('Failed to create a port for network %s') \
+ % data['network_id']
+ LOG.info(msg)
+ redirect = reverse('horizon:admin:networks:detail',
+ args=(data['network_id'],))
+ exceptions.handle(request, msg, redirect=redirect)
+
+
+class UpdatePort(forms.SelfHandlingForm):
+ network_id = forms.CharField(widget=forms.HiddenInput())
+ tenant_id = forms.CharField(widget=forms.HiddenInput())
+ port_id = forms.CharField(widget=forms.HiddenInput())
+ name = forms.CharField(max_length=255,
+ label=_("Name"),
+ required=False)
+ device_id = forms.CharField(max_length=100, label=_("Device ID"),
+ help_text='Device ID attached to the port',
+ required=False)
+
+ def handle(self, request, data):
+ try:
+ LOG.debug('params = %s' % data)
+ port = api.quantum.port_modify(request, data['port_id'],
+ name=data['name'],
+ device_id=data['device_id'])
+ msg = _('Port %s was successfully updated.') % data['port_id']
+ LOG.debug(msg)
+ messages.success(request, msg)
+ return port
+ except Exception:
+ msg = _('Failed to update port %s') % data['port_id']
+ LOG.info(msg)
+ redirect = reverse('horizon:admin:networks:detail',
+ args=[data['network_id']])
+ exceptions.handle(request, msg, redirect=redirect)
diff --git a/openstack_dashboard/dashboards/admin/networks/ports/tables.py b/openstack_dashboard/dashboards/admin/networks/ports/tables.py
new file mode 100644
index 000000000..e8a356256
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/ports/tables.py
@@ -0,0 +1,85 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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.core.urlresolvers import reverse
+from django.utils.translation import ugettext_lazy as _
+
+from horizon import exceptions
+from horizon import tables
+
+from openstack_dashboard import api
+from openstack_dashboard.dashboards.project.networks.ports.tables import \
+ get_fixed_ips, get_attached
+
+
+LOG = logging.getLogger(__name__)
+
+
+class DeletePort(tables.DeleteAction):
+ data_type_singular = _("Port")
+ data_type_plural = _("Ports")
+
+ def delete(self, request, obj_id):
+ try:
+ api.quantum.port_delete(request, obj_id)
+ except:
+ msg = _('Failed to delete subnet %s') % obj_id
+ LOG.info(msg)
+ network_id = self.table.kwargs['network_id']
+ redirect = reverse('horizon:admin:networks:detail',
+ args=[network_id])
+ exceptions.handle(request, msg, redirect=redirect)
+
+
+class CreatePort(tables.LinkAction):
+ name = "create"
+ verbose_name = _("Create Port")
+ url = "horizon:admin:networks:addport"
+ classes = ("ajax-modal", "btn-create")
+
+ def get_link_url(self, datum=None):
+ network_id = self.table.kwargs['network_id']
+ return reverse(self.url, args=(network_id,))
+
+
+class UpdatePort(tables.LinkAction):
+ name = "update"
+ verbose_name = _("Edit Port")
+ url = "horizon:admin:networks:editport"
+ classes = ("ajax-modal", "btn-edit")
+
+ def get_link_url(self, port):
+ network_id = self.table.kwargs['network_id']
+ return reverse(self.url, args=(network_id, port.id))
+
+
+class PortsTable(tables.DataTable):
+ name = tables.Column("name",
+ verbose_name=_("Name"),
+ link="horizon:admin:networks:ports:detail")
+ fixed_ips = tables.Column(get_fixed_ips, verbose_name=_("Fixed IPs"))
+ device_id = tables.Column(get_attached, verbose_name=_("Device Attached"))
+ status = tables.Column("status", verbose_name=_("Status"))
+ admin_state = tables.Column("admin_state",
+ verbose_name=_("Admin State"))
+
+ class Meta:
+ name = "ports"
+ verbose_name = _("Ports")
+ table_actions = (CreatePort, DeletePort)
+ row_actions = (UpdatePort, DeletePort,)
diff --git a/openstack_dashboard/dashboards/admin/networks/ports/tabs.py b/openstack_dashboard/dashboards/admin/networks/ports/tabs.py
new file mode 100644
index 000000000..2fda2ba95
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/ports/tabs.py
@@ -0,0 +1,49 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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.core.urlresolvers import reverse
+from django.utils.translation import ugettext_lazy as _
+
+from horizon import exceptions
+from horizon import tabs
+
+from openstack_dashboard import api
+
+
+LOG = logging.getLogger(__name__)
+
+
+class OverviewTab(tabs.Tab):
+ name = _("Overview")
+ slug = "overview"
+ template_name = "project/networks/ports/_detail_overview.html"
+
+ def get_context_data(self, request):
+ port_id = self.tab_group.kwargs['port_id']
+ try:
+ port = api.quantum.port_get(self.request, port_id)
+ except:
+ redirect = reverse('horizon:admin:networks:index')
+ msg = _('Unable to retrieve port details.')
+ exceptions.handle(request, msg, redirect=redirect)
+ return {'port': port}
+
+
+class PortDetailTabs(tabs.TabGroup):
+ slug = "port_details"
+ tabs = (OverviewTab,)
diff --git a/openstack_dashboard/dashboards/admin/networks/ports/urls.py b/openstack_dashboard/dashboards/admin/networks/ports/urls.py
new file mode 100644
index 000000000..b485427b3
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/ports/urls.py
@@ -0,0 +1,28 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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 openstack_dashboard.dashboards.project.networks.ports.views import \
+ DetailView
+
+PORTS = r'^(?P<port_id>[^/]+)/%s$'
+VIEW_MOD = 'openstack_dashboard.dashboards.admin.networks.ports.views'
+
+
+urlpatterns = patterns(VIEW_MOD,
+ url(PORTS % 'detail', DetailView.as_view(), name='detail')
+)
diff --git a/openstack_dashboard/dashboards/admin/networks/ports/views.py b/openstack_dashboard/dashboards/admin/networks/ports/views.py
new file mode 100644
index 000000000..43fad6390
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/ports/views.py
@@ -0,0 +1,99 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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.core.urlresolvers import reverse
+from django.utils.translation import ugettext_lazy as _
+
+from horizon import exceptions
+from horizon import forms
+
+from openstack_dashboard import api
+from .forms import CreatePort, UpdatePort
+
+LOG = logging.getLogger(__name__)
+
+
+class CreateView(forms.ModalFormView):
+ form_class = CreatePort
+ template_name = 'admin/networks/ports/create.html'
+ success_url = 'horizon:admin:networks:detail'
+
+ def get_success_url(self):
+ return reverse(self.success_url,
+ args=(self.kwargs['network_id'],))
+
+ def get_object(self):
+ if not hasattr(self, "_object"):
+ try:
+ network_id = self.kwargs["network_id"]
+ self._object = api.quantum.network_get(self.request,
+ network_id)
+ except:
+ redirect = reverse("horizon:admin:networks:detail",
+ args=(self.kwargs['network_id'],))
+ msg = _("Unable to retrieve network.")
+ exceptions.handle(self.request, msg, redirect=redirect)
+ return self._object
+
+ def get_context_data(self, **kwargs):
+ context = super(CreateView, self).get_context_data(**kwargs)
+ context['network'] = self.get_object()
+ return context
+
+ def get_initial(self):
+ network = self.get_object()
+ return {"network_id": self.kwargs['network_id'],
+ "network_name": network.name}
+
+
+class UpdateView(forms.ModalFormView):
+ form_class = UpdatePort
+ template_name = 'admin/networks/ports/update.html'
+ context_object_name = 'port'
+ success_url = 'horizon:admin:networks:detail'
+
+ def get_success_url(self):
+ return reverse(self.success_url,
+ args=(self.kwargs['network_id'],))
+
+ def _get_object(self, *args, **kwargs):
+ if not hasattr(self, "_object"):
+ port_id = self.kwargs['port_id']
+ try:
+ self._object = api.quantum.port_get(self.request, port_id)
+ except:
+ redirect = reverse("horizon:admin:networks:detail",
+ args=(self.kwargs['network_id'],))
+ msg = _('Unable to retrieve port details')
+ exceptions.handle(self.request, msg, redirect=redirect)
+ return self._object
+
+ def get_context_data(self, **kwargs):
+ context = super(UpdateView, self).get_context_data(**kwargs)
+ port = self._get_object()
+ context['port_id'] = port['id']
+ context['network_id'] = port['network_id']
+ return context
+
+ def get_initial(self):
+ port = self._get_object()
+ return {'port_id': port['id'],
+ 'network_id': port['network_id'],
+ 'tenant_id': port['tenant_id'],
+ 'name': port['name'],
+ 'device_id': port['device_id']}
diff --git a/openstack_dashboard/dashboards/admin/networks/subnets/__init__.py b/openstack_dashboard/dashboards/admin/networks/subnets/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/subnets/__init__.py
diff --git a/openstack_dashboard/dashboards/admin/networks/subnets/forms.py b/openstack_dashboard/dashboards/admin/networks/subnets/forms.py
new file mode 100644
index 000000000..2a4ab362d
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/subnets/forms.py
@@ -0,0 +1,53 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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.core.urlresolvers import reverse
+from django.utils.translation import ugettext_lazy as _
+
+from horizon import forms
+from horizon import exceptions
+
+from openstack_dashboard import api
+from openstack_dashboard.dashboards.project.networks.subnets import \
+ forms as user_forms
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CreateSubnet(user_forms.CreateSubnet):
+ failure_url = 'horizon:admin:networks:detail'
+
+ def handle(self, request, data):
+ try:
+ # We must specify tenant_id of the network which a subnet is
+ # created for if admin user does not belong to the tenant.
+ network = api.quantum.network_get(request, data['network_id'])
+ data['tenant_id'] = network.tenant_id
+ except:
+ msg = _('Failed to retrieve network %s for a subnet') \
+ % data['network_id']
+ LOG.info(msg)
+ redirect = reverse(self.failure_url, args=[data['network_id']])
+ exceptions.handle(request, msg, redirect=redirect)
+ return super(CreateSubnet, self).handle(request, data)
+
+
+class UpdateSubnet(user_forms.UpdateSubnet):
+ tenant_id = forms.CharField(widget=forms.HiddenInput())
+ failure_url = 'horizon:admin:networks:detail'
diff --git a/openstack_dashboard/dashboards/admin/networks/subnets/tables.py b/openstack_dashboard/dashboards/admin/networks/subnets/tables.py
new file mode 100644
index 000000000..874f26eb1
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/subnets/tables.py
@@ -0,0 +1,83 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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.core.urlresolvers import reverse
+from django.utils.translation import ugettext_lazy as _
+
+from horizon import exceptions
+from horizon import tables
+
+from openstack_dashboard import api
+
+
+LOG = logging.getLogger(__name__)
+
+
+class DeleteSubnet(tables.DeleteAction):
+ data_type_singular = _("Subnet")
+ data_type_plural = _("Subnets")
+
+ def delete(self, request, obj_id):
+ try:
+ api.quantum.subnet_delete(request, obj_id)
+ except:
+ msg = _('Failed to delete subnet %s') % obj_id
+ LOG.info(msg)
+ network_id = self.table.kwargs['network_id']
+ redirect = reverse('horizon:admin:networks:detail',
+ args=[network_id])
+ exceptions.handle(request, msg, redirect=redirect)
+
+
+class CreateSubnet(tables.LinkAction):
+ name = "create"
+ verbose_name = _("Create Subnet")
+ url = "horizon:admin:networks:addsubnet"
+ classes = ("ajax-modal", "btn-create")
+
+ def get_link_url(self, datum=None):
+ network_id = self.table.kwargs['network_id']
+ return reverse(self.url, args=(network_id,))
+
+
+class UpdateSubnet(tables.LinkAction):
+ name = "update"
+ verbose_name = _("Edit Subnet")
+ url = "horizon:admin:networks:editsubnet"
+ classes = ("ajax-modal", "btn-edit")
+
+ def get_link_url(self, subnet):
+ network_id = self.table.kwargs['network_id']
+ return reverse(self.url, args=(network_id, subnet.id))
+
+
+class SubnetsTable(tables.DataTable):
+ name = tables.Column("name", verbose_name=_("Name"),
+ link='horizon:admin:networks:subnets:detail')
+ cidr = tables.Column("cidr", verbose_name=_("CIDR"))
+ ip_version = tables.Column("ipver_str", verbose_name=_("IP Version"))
+ gateway_ip = tables.Column("gateway_ip", verbose_name=_("Gateway IP"))
+
+ def get_object_display(self, subnet):
+ return subnet.id
+
+ class Meta:
+ name = "subnets"
+ verbose_name = _("Subnets")
+ table_actions = (CreateSubnet, DeleteSubnet)
+ row_actions = (UpdateSubnet, DeleteSubnet,)
diff --git a/openstack_dashboard/dashboards/admin/networks/subnets/urls.py b/openstack_dashboard/dashboards/admin/networks/subnets/urls.py
new file mode 100644
index 000000000..6af5d626c
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/subnets/urls.py
@@ -0,0 +1,29 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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 openstack_dashboard.dashboards.project.networks.subnets.views import \
+ DetailView
+
+
+SUBNETS = r'^(?P<subnet_id>[^/]+)/%s$'
+VIEW_MOD = 'openstack_dashboard.dashboards.admin.networks.subnets.views'
+
+
+urlpatterns = patterns(VIEW_MOD,
+ url(SUBNETS % 'detail', DetailView.as_view(), name='detail')
+)
diff --git a/openstack_dashboard/dashboards/admin/networks/subnets/views.py b/openstack_dashboard/dashboards/admin/networks/subnets/views.py
new file mode 100644
index 000000000..86837cfac
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/subnets/views.py
@@ -0,0 +1,103 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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.core.urlresolvers import reverse
+from django.utils.translation import ugettext_lazy as _
+
+from horizon import exceptions
+from horizon import forms
+
+from openstack_dashboard import api
+from .forms import CreateSubnet, UpdateSubnet
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CreateView(forms.ModalFormView):
+ form_class = CreateSubnet
+ template_name = 'admin/networks/subnets/create.html'
+ success_url = 'horizon:admin:networks:detail'
+
+ def get_success_url(self):
+ return reverse(self.success_url,
+ args=(self.kwargs['network_id'],))
+
+ def get_object(self):
+ if not hasattr(self, "_object"):
+ try:
+ network_id = self.kwargs["network_id"]
+ self._object = api.quantum.network_get(self.request,
+ network_id)
+ except:
+ redirect = reverse('horizon:project:networks:index')
+ msg = _("Unable to retrieve network.")
+ exceptions.handle(self.request, msg, redirect=redirect)
+ return self._object
+
+ def get_context_data(self, **kwargs):
+ context = super(CreateView, self).get_context_data(**kwargs)
+ context['network'] = self.get_object()
+ return context
+
+ def get_initial(self):
+ network = self.get_object()
+ return {"network_id": self.kwargs['network_id'],
+ "network_name": network.name}
+
+
+class UpdateView(forms.ModalFormView):
+ form_class = UpdateSubnet
+ template_name = 'admin/networks/subnets/update.html'
+ context_object_name = 'subnet'
+ success_url = 'horizon:admin:networks:detail'
+
+ def get_success_url(self):
+ return reverse(self.success_url,
+ args=(self.kwargs['network_id'],))
+
+ def _get_object(self, *args, **kwargs):
+ if not hasattr(self, "_object"):
+ subnet_id = self.kwargs['subnet_id']
+ try:
+ self._object = api.quantum.subnet_get(self.request, subnet_id)
+ except:
+ redirect = reverse("horizon:admin:networks:detail",
+ args=(self.kwargs['network_id'],))
+ msg = _('Unable to retrieve subnet details')
+ exceptions.handle(self.request, msg, redirect=redirect)
+ return self._object
+
+ def get_context_data(self, **kwargs):
+ context = super(UpdateView, self).get_context_data(**kwargs)
+ subnet = self._get_object()
+ context['subnet_id'] = subnet['id']
+ context['network_id'] = subnet['network_id']
+ context['cidr'] = subnet['cidr']
+ context['ip_version'] = {4: 'IPv4', 6: 'IPv6'}[subnet['ip_version']]
+ return context
+
+ def get_initial(self):
+ subnet = self._get_object()
+ return {'network_id': self.kwargs['network_id'],
+ 'subnet_id': subnet['id'],
+ 'tenant_id': subnet['tenant_id'],
+ 'cidr': subnet['cidr'],
+ 'ip_version': subnet['ip_version'],
+ 'name': subnet['name'],
+ 'gateway_ip': subnet['gateway_ip']}
diff --git a/openstack_dashboard/dashboards/admin/networks/tables.py b/openstack_dashboard/dashboards/admin/networks/tables.py
new file mode 100644
index 000000000..9f85d714d
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/tables.py
@@ -0,0 +1,82 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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.core.urlresolvers import reverse
+from django.template import defaultfilters as filters
+from django.utils.translation import ugettext_lazy as _
+
+from horizon import exceptions
+from horizon import tables
+
+from openstack_dashboard import api
+from openstack_dashboard.dashboards.project.networks.tables import get_subnets
+
+
+LOG = logging.getLogger(__name__)
+
+
+class DeleteNetwork(tables.DeleteAction):
+ data_type_singular = _("Network")
+ data_type_plural = _("Networks")
+
+ def delete(self, request, obj_id):
+ try:
+ api.quantum.network_delete(request, obj_id)
+ except:
+ msg = _('Failed to delete network %s') % obj_id
+ LOG.info(msg)
+ redirect = reverse('horizon:admin:networks:index')
+ exceptions.handle(request, msg, redirect=redirect)
+
+
+class CreateNetwork(tables.LinkAction):
+ name = "create"
+ verbose_name = _("Create Network")
+ url = "horizon:admin:networks:create"
+ classes = ("ajax-modal", "btn-create")
+
+
+class EditNetwork(tables.LinkAction):
+ name = "update"
+ verbose_name = _("Edit Network")
+ url = "horizon:admin:networks:update"
+ classes = ("ajax-modal", "btn-edit")
+
+
+#def _get_subnets(network):
+# cidrs = [subnet.get('cidr') for subnet in network.subnets]
+# return ','.join(cidrs)
+
+
+class NetworksTable(tables.DataTable):
+ tenant = tables.Column("tenant_name", verbose_name=_("Project"))
+ name = tables.Column("name", verbose_name=_("Network Name"),
+ link='horizon:admin:networks:detail')
+ subnets = tables.Column(get_subnets,
+ verbose_name=_("Subnets Associated"),)
+ shared = tables.Column("shared", verbose_name=_("Shared"),
+ filters=(filters.yesno, filters.capfirst))
+ status = tables.Column("status", verbose_name=_("Status"))
+ admin_state = tables.Column("admin_state",
+ verbose_name=_("Admin State"))
+
+ class Meta:
+ name = "networks"
+ verbose_name = _("Networks")
+ table_actions = (CreateNetwork, DeleteNetwork)
+ row_actions = (EditNetwork, DeleteNetwork)
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/_create.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/_create.html
new file mode 100644
index 000000000..6fda0ca63
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/_create.html
@@ -0,0 +1,25 @@
+{% extends "horizon/common/_modal_form.html" %}
+{% load i18n %}
+
+{% block form_id %}create_network_form{% endblock %}
+{% block form_action %}{% url horizon:admin:networks:create %}{% endblock %}
+
+{% block modal_id %}create_network_modal{% endblock %}
+{% block modal-header %}{% trans "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 "Select a name for your network."%}</p>
+</div>
+{% endblock %}
+
+{% block modal-footer %}
+ <input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Network" %}" />
+ <a href="{% url horizon:admin:networks:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/_update.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/_update.html
new file mode 100644
index 000000000..7ad0c69c3
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/_update.html
@@ -0,0 +1,24 @@
+{% extends "horizon/common/_modal_form.html" %}
+{% load i18n %}
+
+{% block form_id %}update_network_form{% endblock %}
+{% block form_action %}{% url horizon:admin:networks:update network_id %}{% endblock %}
+
+{% block modal-header %}{% trans "Edit 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 "You may update the editable properties of your network here." %}</p>
+</div>
+{% endblock %}
+
+{% block modal-footer %}
+ <input class="btn btn-primary pull-right" type="submit" value="{% trans "Save Changes" %}" />
+ <a href="{% url horizon:admin:networks:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/create.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/create.html
new file mode 100644
index 000000000..975227f4e
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/create.html
@@ -0,0 +1,11 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% block title %}{% trans "Create Network" %}{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" with title=_("Create Network") %}
+{% endblock page_header %}
+
+{% block main %}
+ {% include "admin/networks/_create.html" %}
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/index.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/index.html
new file mode 100644
index 000000000..bcb1b74f8
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/index.html
@@ -0,0 +1,21 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% block title %}{% trans "Networks" %}{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" with title=_("Networks") %}
+{% endblock page_header %}
+
+{% block main %}
+ <div id="networks">
+ {{ networks_table.render }}
+ </div>
+
+ <div id="subnets">
+ {{ subnets_table.render }}
+ </div>
+
+ <div id="ports">
+ {{ ports_table.render }}
+ </div>
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_create.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_create.html
new file mode 100644
index 000000000..a35266149
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_create.html
@@ -0,0 +1,25 @@
+{% extends "horizon/common/_modal_form.html" %}
+{% load i18n %}
+
+{% block form_id %}create_port_form{% endblock %}
+{% block form_action %}{% url horizon:admin:networks:addport network.id %}
+{% endblock %}
+
+{% block modal-header %}{% trans "Create Port" %}{% 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 "You can create a port for the network. If you specify device ID to be attached, the device specified will be attached to the port created."%}</p>
+</div>
+{% endblock %}
+
+{% block modal-footer %}
+ <input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Port" %}" />
+ <a href="{% url horizon:admin:networks:detail network.id %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_update.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_update.html
new file mode 100644
index 000000000..bb3162817
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/_update.html
@@ -0,0 +1,29 @@
+{% extends "horizon/common/_modal_form.html" %}
+{% load i18n %}
+
+{% block form_id %}update_port_form{% endblock %}
+{% block form_action %}{% url horizon:admin:networks:editport network_id port_id %}{% endblock %}
+
+{% block modal-header %}{% trans "Edit Port" %}{% endblock %}
+
+{% block modal-body %}
+<div class="left">
+ <dl>
+ <dt>{% trans "ID" %}</dt>
+ <dd>{{ port_id }}</dd>
+ </dl>
+ <hr>
+ <fieldset>
+ {% include "horizon/common/_form_fields.html" %}
+ </fieldset>
+</div>
+<div class="right">
+ <h3>{% trans "Description:" %}</h3>
+ <p>{% trans "You may update the editable properties of your port here." %}</p>
+</div>
+{% endblock %}
+
+{% block modal-footer %}
+ <input class="btn btn-primary pull-right" type="submit" value="{% trans "Save Changes" %}" />
+ <a href="{% url horizon:admin:networks:detail network_id %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/create.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/create.html
new file mode 100644
index 000000000..e99551300
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/create.html
@@ -0,0 +1,11 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% block title %}{% trans "Create Port" %}{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" with title=_("Create Port") %}
+{% endblock page_header %}
+
+{% block main %}
+ {% include "admin/networks/ports/_create.html" %}
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/update.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/update.html
new file mode 100644
index 000000000..ae0f91cfa
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/ports/update.html
@@ -0,0 +1,11 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% block title %}{% trans "Update Port" %}{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" with title=_("Update Port") %}
+{% endblock page_header %}
+
+{% block main %}
+ {% include 'admin/networks/ports/_update.html' %}
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/_create.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/_create.html
new file mode 100644
index 000000000..c7367b4c8
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/_create.html
@@ -0,0 +1,25 @@
+{% extends "horizon/common/_modal_form.html" %}
+{% load i18n %}
+
+{% block form_id %}create_subnet_form{% endblock %}
+{% block form_action %}{% url horizon:admin:networks:addsubnet network.id %}
+{% endblock %}
+
+{% block modal-header %}{% trans "Create Subnet" %}{% 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 "You can create a subnet for the network. Any network address can be specified unless the network address does not overlap other subnets in the network." %}</p>
+</div>
+{% endblock %}
+
+{% block modal-footer %}
+ <input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Subnet" %}" />
+ <a href="{% url horizon:admin:networks:detail network.id %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/_update.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/_update.html
new file mode 100644
index 000000000..45a125ea6
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/_update.html
@@ -0,0 +1,33 @@
+{% extends "horizon/common/_modal_form.html" %}
+{% load i18n %}
+
+{% block form_id %}update_subnet_form{% endblock %}
+{% block form_action %}{% url horizon:admin:networks:editsubnet network_id subnet_id %}{% endblock %}
+
+{% block modal-header %}{% trans "Edit Subnet" %}{% endblock %}
+
+{% block modal-body %}
+<div class="left">
+ <dl>
+ <dt>{% trans "ID" %}</dt>
+ <dd>{{ subnet_id }}</dd>
+ <dt>{% trans "Network Address" %}</dt>
+ <dd>{{ cidr }}</dd>
+ <dt>{% trans "IP version" %}</dt>
+ <dd>{{ ip_version }}</dd>
+ </dl>
+ <hr>
+ <fieldset>
+ {% include "horizon/common/_form_fields.html" %}
+ </fieldset>
+</div>
+<div class="right">
+ <h3>{% trans "Description:" %}</h3>
+ <p>{% trans "You may update the editable properties of your subnet here." %}</p>
+</div>
+{% endblock %}
+
+{% block modal-footer %}
+ <input class="btn btn-primary pull-right" type="submit" value="{% trans "Save Changes" %}" />
+ <a href="{% url horizon:admin:networks:detail network_id %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/create.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/create.html
new file mode 100644
index 000000000..84624cc13
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/create.html
@@ -0,0 +1,11 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% block title %}{% trans "Create Subnet" %}{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" with title=_("Create Subnet") %}
+{% endblock page_header %}
+
+{% block main %}
+ {% include "admin/networks/subnets/_create.html" %}
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/index.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/index.html
new file mode 100644
index 000000000..9c25d565e
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/index.html
@@ -0,0 +1,11 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% block title %}{% trans "Network Detail" %}{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" with title=_("Network Detail") %}
+{% endblock page_header %}
+
+{% block main %}
+ {{ table.render }}
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/update.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/update.html
new file mode 100644
index 000000000..b152cdf4e
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/subnets/update.html
@@ -0,0 +1,11 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% block title %}{% trans "Update Subnet" %}{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" with title=_("Update Subnet") %}
+{% endblock page_header %}
+
+{% block main %}
+ {% include 'admin/networks/subnets/_update.html' %}
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/templates/networks/update.html b/openstack_dashboard/dashboards/admin/networks/templates/networks/update.html
new file mode 100644
index 000000000..a70a0b1a7
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/templates/networks/update.html
@@ -0,0 +1,11 @@
+{% extends 'base.html' %}
+{% load i18n %}
+{% block title %}{% trans "Update Network" %}{% endblock %}
+
+{% block page_header %}
+ {% include "horizon/common/_page_header.html" with title=_("Update Network") %}
+{% endblock page_header %}
+
+{% block main %}
+ {% include 'admin/networks/_update.html' %}
+{% endblock %}
diff --git a/openstack_dashboard/dashboards/admin/networks/tests.py b/openstack_dashboard/dashboards/admin/networks/tests.py
new file mode 100644
index 000000000..4ea208cae
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/tests.py
@@ -0,0 +1,805 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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 openstack_dashboard import api
+from openstack_dashboard.test import helpers as test
+
+
+INDEX_URL = reverse('horizon:admin:networks:index')
+
+
+class NetworkTests(test.BaseAdminViewTests):
+ @test.create_stubs({api.quantum: ('network_list',),
+ api.keystone: ('tenant_list',)})
+ def test_index(self):
+ tenants = self.tenants.list()
+ api.quantum.network_list(IsA(http.HttpRequest)) \
+ .AndReturn(self.networks.list())
+ api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
+ .AndReturn(tenants)
+
+ self.mox.ReplayAll()
+
+ res = self.client.get(INDEX_URL)
+
+ self.assertTemplateUsed(res, 'admin/networks/index.html')
+ networks = res.context['networks_table'].data
+ self.assertItemsEqual(networks, self.networks.list())
+
+ @test.create_stubs({api.quantum: ('network_list',)})
+ def test_index_network_list_exception(self):
+ api.quantum.network_list(IsA(http.HttpRequest)) \
+ .AndRaise(self.exceptions.quantum)
+
+ self.mox.ReplayAll()
+
+ res = self.client.get(INDEX_URL)
+
+ self.assertTemplateUsed(res, 'admin/networks/index.html')
+ self.assertEqual(len(res.context['networks_table'].data), 0)
+ self.assertMessageCount(res, error=1)
+
+ @test.create_stubs({api.quantum: ('network_get',
+ 'subnet_list',
+ 'port_list',)})
+ def test_network_detail(self):
+ network_id = self.networks.first().id
+ api.quantum.network_get(IsA(http.HttpRequest), network_id)\
+ .AndReturn(self.networks.first())
+ api.quantum.subnet_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.subnets.first()])
+ api.quantum.port_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.ports.first()])
+
+ self.mox.ReplayAll()
+
+ res = self.client.get(reverse('horizon:admin:networks:detail',
+ args=[network_id]))
+
+ self.assertTemplateUsed(res, 'project/networks/detail.html')
+ subnets = res.context['subnets_table'].data
+ ports = res.context['ports_table'].data
+ self.assertItemsEqual(subnets, [self.subnets.first()])
+ self.assertItemsEqual(ports, [self.ports.first()])
+
+ @test.create_stubs({api.quantum: ('network_get',
+ 'subnet_list',
+ 'port_list',)})
+ def test_network_detail_network_exception(self):
+ network_id = self.networks.first().id
+ api.quantum.network_get(IsA(http.HttpRequest), network_id)\
+ .AndRaise(self.exceptions.quantum)
+ api.quantum.subnet_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.subnets.first()])
+ api.quantum.port_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.ports.first()])
+
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:networks:detail', args=[network_id])
+ res = self.client.get(url)
+
+ redir_url = INDEX_URL
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('network_get',
+ 'subnet_list',
+ 'port_list',)})
+ def test_network_detail_subnet_exception(self):
+ network_id = self.networks.first().id
+ api.quantum.network_get(IsA(http.HttpRequest), network_id).\
+ AndReturn(self.networks.first())
+ api.quantum.subnet_list(IsA(http.HttpRequest), network_id=network_id).\
+ AndRaise(self.exceptions.quantum)
+ api.quantum.port_list(IsA(http.HttpRequest), network_id=network_id).\
+ AndReturn([self.ports.first()])
+
+ self.mox.ReplayAll()
+
+ res = self.client.get(reverse('horizon:admin:networks:detail',
+ args=[network_id]))
+
+ self.assertTemplateUsed(res, 'project/networks/detail.html')
+ subnets = res.context['subnets_table'].data
+ ports = res.context['ports_table'].data
+ self.assertEqual(len(subnets), 0)
+ self.assertItemsEqual(ports, [self.ports.first()])
+
+ @test.create_stubs({api.quantum: ('network_get',
+ 'subnet_list',
+ 'port_list',)})
+ def test_network_detail_port_exception(self):
+ network_id = self.networks.first().id
+ api.quantum.network_get(IsA(http.HttpRequest), network_id).\
+ AndReturn(self.networks.first())
+ api.quantum.subnet_list(IsA(http.HttpRequest), network_id=network_id).\
+ AndReturn([self.subnets.first()])
+ api.quantum.port_list(IsA(http.HttpRequest), network_id=network_id).\
+ AndRaise(self.exceptions.quantum)
+
+ self.mox.ReplayAll()
+
+ res = self.client.get(reverse('horizon:admin:networks:detail',
+ args=[network_id]))
+
+ self.assertTemplateUsed(res, 'project/networks/detail.html')
+ subnets = res.context['subnets_table'].data
+ ports = res.context['ports_table'].data
+ self.assertItemsEqual(subnets, [self.subnets.first()])
+ self.assertEqual(len(ports), 0)
+
+ @test.create_stubs({api.keystone: ('tenant_list',)})
+ def test_network_create_get(self):
+ tenants = self.tenants.list()
+ api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
+ .AndReturn(tenants)
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:networks:create')
+ res = self.client.get(url)
+
+ self.assertTemplateUsed(res, 'admin/networks/create.html')
+
+ @test.create_stubs({api.quantum: ('network_create',),
+ api.keystone: ('tenant_list',)})
+ def test_network_create_post(self):
+ tenants = self.tenants.list()
+ tenant_id = self.tenants.first().id
+ network = self.networks.first()
+ api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
+ .AndReturn(tenants)
+ api.quantum.network_create(IsA(http.HttpRequest), name=network.name,
+ tenant_id=tenant_id, shared=True)\
+ .AndReturn(network)
+ self.mox.ReplayAll()
+
+ form_data = {'tenant_id': tenant_id,
+ 'name': network.name,
+ 'shared': True}
+ url = reverse('horizon:admin:networks:create')
+ res = self.client.post(url, form_data)
+
+ self.assertNoFormErrors(res)
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+
+ @test.create_stubs({api.quantum: ('network_create',),
+ api.keystone: ('tenant_list',)})
+ def test_network_create_post_network_exception(self):
+ tenants = self.tenants.list()
+ tenant_id = self.tenants.first().id
+ network = self.networks.first()
+ api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
+ .AndReturn(tenants)
+ api.quantum.network_create(IsA(http.HttpRequest), name=network.name,
+ tenant_id=tenant_id, shared=False)\
+ .AndRaise(self.exceptions.quantum)
+ self.mox.ReplayAll()
+
+ form_data = {'tenant_id': tenant_id,
+ 'name': network.name,
+ 'shared': False}
+ url = reverse('horizon:admin:networks:create')
+ res = self.client.post(url, form_data)
+
+ self.assertNoFormErrors(res)
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+
+ @test.create_stubs({api.quantum: ('network_get',)})
+ def test_network_update_get(self):
+ network = self.networks.first()
+ api.quantum.network_get(IsA(http.HttpRequest), network.id)\
+ .AndReturn(network)
+
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:networks:update', args=[network.id])
+ res = self.client.get(url)
+
+ self.assertTemplateUsed(res, 'admin/networks/update.html')
+
+ @test.create_stubs({api.quantum: ('network_get',)})
+ def test_network_update_get_exception(self):
+ network = self.networks.first()
+ api.quantum.network_get(IsA(http.HttpRequest), network.id)\
+ .AndRaise(self.exceptions.quantum)
+
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:networks:update', args=[network.id])
+ res = self.client.get(url)
+
+ redir_url = INDEX_URL
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('network_modify',
+ 'network_get',)})
+ def test_network_update_post(self):
+ network = self.networks.first()
+ api.quantum.network_modify(IsA(http.HttpRequest), network.id,
+ name=network.name, shared=True)\
+ .AndReturn(network)
+ api.quantum.network_get(IsA(http.HttpRequest), network.id)\
+ .AndReturn(network)
+ self.mox.ReplayAll()
+
+ formData = {'network_id': network.id,
+ 'name': network.name,
+ 'tenant_id': network.tenant_id,
+ 'shared': True}
+ url = reverse('horizon:admin:networks:update', args=[network.id])
+ res = self.client.post(url, formData)
+
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+
+ @test.create_stubs({api.quantum: ('network_modify',
+ 'network_get',)})
+ def test_network_update_post_exception(self):
+ network = self.networks.first()
+ api.quantum.network_modify(IsA(http.HttpRequest), network.id,
+ name=network.name, shared=False)\
+ .AndRaise(self.exceptions.quantum)
+ api.quantum.network_get(IsA(http.HttpRequest), network.id)\
+ .AndReturn(network)
+ self.mox.ReplayAll()
+
+ form_data = {'network_id': network.id,
+ 'name': network.name,
+ 'tenant_id': network.tenant_id,
+ 'shared': False}
+ url = reverse('horizon:admin:networks:update', args=[network.id])
+ res = self.client.post(url, form_data)
+
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+
+ @test.create_stubs({api.quantum: ('network_list',
+ 'network_delete'),
+ api.keystone: ('tenant_list',)})
+ def test_delete_network(self):
+ tenants = self.tenants.list()
+ network = self.networks.first()
+ api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
+ .AndReturn(tenants)
+ api.quantum.network_list(IsA(http.HttpRequest))\
+ .AndReturn([network])
+ api.quantum.network_delete(IsA(http.HttpRequest), network.id)
+
+ self.mox.ReplayAll()
+
+ form_data = {'action': 'networks__delete__%s' % network.id}
+ res = self.client.post(INDEX_URL, form_data)
+
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+
+ @test.create_stubs({api.quantum: ('network_list',
+ 'network_delete'),
+ api.keystone: ('tenant_list',)})
+ def test_delete_network_exception(self):
+ tenants = self.tenants.list()
+ network = self.networks.first()
+ api.keystone.tenant_list(IsA(http.HttpRequest), admin=True)\
+ .AndReturn(tenants)
+ api.quantum.network_list(IsA(http.HttpRequest))\
+ .AndReturn([network])
+ api.quantum.network_delete(IsA(http.HttpRequest), network.id)\
+ .AndRaise(self.exceptions.quantum)
+
+ self.mox.ReplayAll()
+
+ form_data = {'action': 'networks__delete__%s' % network.id}
+ res = self.client.post(INDEX_URL, form_data)
+
+ self.assertRedirectsNoFollow(res, INDEX_URL)
+
+ @test.create_stubs({api.quantum: ('subnet_get',)})
+ def test_subnet_detail(self):
+ subnet = self.subnets.first()
+ api.quantum.subnet_get(IsA(http.HttpRequest), subnet.id)\
+ .AndReturn(self.subnets.first())
+
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:networks:subnets:detail',
+ args=[subnet.id])
+ res = self.client.get(url)
+
+ self.assertTemplateUsed(res, 'project/networks/subnets/detail.html')
+ self.assertEqual(res.context['subnet'].id, subnet.id)
+
+ @test.create_stubs({api.quantum: ('subnet_get',)})
+ def test_subnet_detail_exception(self):
+ subnet = self.subnets.first()
+ api.quantum.subnet_get(IsA(http.HttpRequest), subnet.id)\
+ .AndRaise(self.exceptions.quantum)
+
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:networks:subnets:detail',
+ args=[subnet.id])
+ res = self.client.get(url)
+
+ # admin DetailView is shared with userpanel one, so
+ # redirection URL on error is userpanel index.
+ redir_url = reverse('horizon:project:networks:index')
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('network_get',)})
+ def test_subnet_create_get(self):
+ network = self.networks.first()
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:networks:addsubnet',
+ args=[network.id])
+ res = self.client.get(url)
+
+ self.assertTemplateUsed(res, 'admin/networks/subnets/create.html')
+
+ @test.create_stubs({api.quantum: ('network_get',
+ 'subnet_create',)})
+ def test_subnet_create_post(self):
+ network = self.networks.first()
+ subnet = self.subnets.first()
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ api.quantum.subnet_create(IsA(http.HttpRequest),
+ network_id=network.id,
+ network_name=network.name,
+ name=subnet.name,
+ cidr=subnet.cidr,
+ ip_version=subnet.ip_version,
+ gateway_ip=subnet.gateway_ip,
+ tenant_id=subnet.tenant_id)\
+ .AndReturn(subnet)
+ self.mox.ReplayAll()
+
+ form_data = {'network_id': subnet.network_id,
+ 'network_name': network.name,
+ 'name': subnet.name,
+ 'cidr': subnet.cidr,
+ 'ip_version': subnet.ip_version,
+ 'gateway_ip': subnet.gateway_ip}
+ url = reverse('horizon:admin:networks:addsubnet',
+ args=[subnet.network_id])
+ res = self.client.post(url, form_data)
+
+ self.assertNoFormErrors(res)
+ redir_url = reverse('horizon:admin:networks:detail',
+ args=[subnet.network_id])
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('network_get',
+ 'subnet_create',)})
+ def test_subnet_create_post_network_exception(self):
+ network = self.networks.first()
+ subnet = self.subnets.first()
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndRaise(self.exceptions.quantum)
+ self.mox.ReplayAll()
+
+ form_data = {'network_id': subnet.network_id,
+ 'network_name': network.name,
+ 'name': subnet.name,
+ 'cidr': subnet.cidr,
+ 'ip_version': subnet.ip_version,
+ 'gateway_ip': subnet.gateway_ip}
+ url = reverse('horizon:admin:networks:addsubnet',
+ args=[subnet.network_id])
+ res = self.client.post(url, form_data)
+
+ self.assertNoFormErrors(res)
+ # admin DetailView is shared with userpanel one, so
+ # redirection URL on error is userpanel index.
+ redir_url = reverse('horizon:project:networks:index')
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('network_get',
+ 'subnet_create',)})
+ def test_subnet_create_post_subnet_exception(self):
+ network = self.networks.first()
+ subnet = self.subnets.first()
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ api.quantum.subnet_create(IsA(http.HttpRequest),
+ network_id=network.id,
+ network_name=network.name,
+ name=subnet.name,
+ cidr=subnet.cidr,
+ ip_version=subnet.ip_version,
+ gateway_ip=subnet.gateway_ip,
+ tenant_id=subnet.tenant_id)\
+ .AndRaise(self.exceptions.quantum)
+ self.mox.ReplayAll()
+
+ form_data = {'network_id': subnet.network_id,
+ 'network_name': network.name,
+ 'name': subnet.name,
+ 'cidr': subnet.cidr,
+ 'ip_version': subnet.ip_version,
+ 'gateway_ip': subnet.gateway_ip}
+ url = reverse('horizon:admin:networks:addsubnet',
+ args=[subnet.network_id])
+ res = self.client.post(url, form_data)
+
+ redir_url = reverse('horizon:admin:networks:detail',
+ args=[subnet.network_id])
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('network_get',)})
+ def test_subnet_create_post_cidr_inconsistent(self):
+ network = self.networks.first()
+ subnet = self.subnets.first()
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ self.mox.ReplayAll()
+
+ # dummy IPv6 address
+ cidr = '2001:0DB8:0:CD30:123:4567:89AB:CDEF/60'
+ form_data = {'network_id': subnet.network_id,
+ 'network_name': network.name,
+ 'name': subnet.name,
+ 'cidr': cidr,
+ 'ip_version': subnet.ip_version,
+ 'gateway_ip': subnet.gateway_ip}
+ url = reverse('horizon:admin:networks:addsubnet',
+ args=[subnet.network_id])
+ res = self.client.post(url, form_data)
+
+ expected_msg = 'Network Address and IP version are inconsistent.'
+ self.assertContains(res, expected_msg)
+
+ @test.create_stubs({api.quantum: ('network_get',)})
+ def test_subnet_create_post_gw_inconsistent(self):
+ network = self.networks.first()
+ subnet = self.subnets.first()
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ self.mox.ReplayAll()
+
+ # dummy IPv6 address
+ gateway_ip = '2001:0DB8:0:CD30:123:4567:89AB:CDEF'
+ form_data = {'network_id': subnet.network_id,
+ 'network_name': network.name,
+ 'name': subnet.name,
+ 'cidr': subnet.cidr,
+ 'ip_version': subnet.ip_version,
+ 'gateway_ip': gateway_ip}
+ url = reverse('horizon:admin:networks:addsubnet',
+ args=[subnet.network_id])
+ res = self.client.post(url, form_data)
+
+ self.assertContains(res, 'Gateway IP and IP version are inconsistent.')
+
+ @test.create_stubs({api.quantum: ('subnet_modify',
+ 'subnet_get',)})
+ def test_subnet_update_post(self):
+ subnet = self.subnets.first()
+ api.quantum.subnet_get(IsA(http.HttpRequest), subnet.id)\
+ .AndReturn(subnet)
+ api.quantum.subnet_modify(IsA(http.HttpRequest), subnet.id,
+ name=subnet.name,
+ gateway_ip=subnet.gateway_ip)\
+ .AndReturn(subnet)
+ self.mox.ReplayAll()
+
+ formData = {'network_id': subnet.network_id,
+ 'tenant_id': subnet.tenant_id,
+ 'subnet_id': subnet.id,
+ 'name': subnet.name,
+ 'cidr': subnet.cidr,
+ 'ip_version': subnet.ip_version,
+ 'gateway_ip': subnet.gateway_ip}
+ url = reverse('horizon:admin:networks:editsubnet',
+ args=[subnet.network_id, subnet.id])
+ res = self.client.post(url, formData)
+
+ redir_url = reverse('horizon:admin:networks:detail',
+ args=[subnet.network_id])
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('subnet_modify',
+ 'subnet_get',)})
+ def test_subnet_update_post_gw_inconsistent(self):
+ subnet = self.subnets.first()
+ api.quantum.subnet_get(IsA(http.HttpRequest), subnet.id)\
+ .AndReturn(subnet)
+ self.mox.ReplayAll()
+
+ # dummy IPv6 address
+ gateway_ip = '2001:0DB8:0:CD30:123:4567:89AB:CDEF'
+ formData = {'network_id': subnet.network_id,
+ 'tenant_id': subnet.tenant_id,
+ 'subnet_id': subnet.id,
+ 'name': subnet.name,
+ 'cidr': subnet.cidr,
+ 'ip_version': subnet.ip_version,
+ 'gateway_ip': gateway_ip}
+ url = reverse('horizon:admin:networks:editsubnet',
+ args=[subnet.network_id, subnet.id])
+ res = self.client.post(url, formData)
+
+ self.assertContains(res, 'Gateway IP and IP version are inconsistent.')
+
+ @test.create_stubs({api.quantum: ('subnet_delete',
+ 'subnet_list',
+ 'port_list',)})
+ def test_subnet_delete(self):
+ subnet = self.subnets.first()
+ network_id = subnet.network_id
+ api.quantum.subnet_delete(IsA(http.HttpRequest), subnet.id)
+ api.quantum.subnet_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.subnets.first()])
+ api.quantum.port_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.ports.first()])
+ self.mox.ReplayAll()
+
+ formData = {'action': 'subnets__delete__%s' % subnet.id}
+ url = reverse('horizon:admin:networks:detail',
+ args=[network_id])
+ res = self.client.post(url, formData)
+
+ self.assertRedirectsNoFollow(res, url)
+
+ @test.create_stubs({api.quantum: ('subnet_delete',
+ 'subnet_list',
+ 'port_list',)})
+ def test_subnet_delete_exception(self):
+ subnet = self.subnets.first()
+ network_id = subnet.network_id
+ api.quantum.subnet_delete(IsA(http.HttpRequest), subnet.id)\
+ .AndRaise(self.exceptions.quantum)
+ api.quantum.subnet_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.subnets.first()])
+ api.quantum.port_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.ports.first()])
+ self.mox.ReplayAll()
+
+ formData = {'action': 'subnets__delete__%s' % subnet.id}
+ url = reverse('horizon:admin:networks:detail',
+ args=[network_id])
+ res = self.client.post(url, formData)
+
+ self.assertRedirectsNoFollow(res, url)
+
+ @test.create_stubs({api.quantum: ('port_get',)})
+ def test_port_detail(self):
+ port = self.ports.first()
+ api.quantum.port_get(IsA(http.HttpRequest), port.id)\
+ .AndReturn(self.ports.first())
+
+ self.mox.ReplayAll()
+
+ res = self.client.get(reverse('horizon:admin:networks:ports:detail',
+ args=[port.id]))
+
+ self.assertTemplateUsed(res, 'project/networks/ports/detail.html')
+ self.assertEqual(res.context['port'].id, port.id)
+
+ @test.create_stubs({api.quantum: ('port_get',)})
+ def test_port_detail_exception(self):
+ port = self.ports.first()
+ api.quantum.port_get(IsA(http.HttpRequest), port.id)\
+ .AndRaise(self.exceptions.quantum)
+
+ self.mox.ReplayAll()
+
+ res = self.client.get(reverse('horizon:admin:networks:ports:detail',
+ args=[port.id]))
+
+ # admin DetailView is shared with userpanel one, so
+ # redirection URL on error is userpanel index.
+ redir_url = reverse('horizon:project:networks:index')
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('network_get',)})
+ def test_port_create_get(self):
+ network = self.networks.first()
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:networks:addport',
+ args=[network.id])
+ res = self.client.get(url)
+
+ self.assertTemplateUsed(res, 'admin/networks/ports/create.html')
+
+ @test.create_stubs({api.quantum: ('network_get',
+ 'port_create')})
+ def test_port_create_post(self):
+ network = self.networks.first()
+ port = self.ports.first()
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ api.quantum.port_create(IsA(http.HttpRequest),
+ tenant_id=network.tenant_id,
+ network_id=network.id,
+ network_name=network.name,
+ name=port.name,
+ device_id=port.device_id)\
+ .AndReturn(port)
+ self.mox.ReplayAll()
+
+ form_data = {'network_id': port.network_id,
+ 'network_name': network.name,
+ 'name': port.name,
+ 'device_id': port.device_id}
+ url = reverse('horizon:admin:networks:addport',
+ args=[port.network_id])
+ res = self.client.post(url, form_data)
+
+ self.assertNoFormErrors(res)
+ redir_url = reverse('horizon:admin:networks:detail',
+ args=[port.network_id])
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('network_get',
+ 'port_create')})
+ def test_port_create_post_exception(self):
+ network = self.networks.first()
+ port = self.ports.first()
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ api.quantum.network_get(IsA(http.HttpRequest),
+ network.id)\
+ .AndReturn(self.networks.first())
+ api.quantum.port_create(IsA(http.HttpRequest),
+ tenant_id=network.tenant_id,
+ network_id=network.id,
+ network_name=network.name,
+ name=port.name,
+ device_id=port.device_id)\
+ .AndRaise(self.exceptions.quantum)
+ self.mox.ReplayAll()
+
+ form_data = {'network_id': port.network_id,
+ 'network_name': network.name,
+ 'name': port.name,
+ 'device_id': port.device_id}
+ url = reverse('horizon:admin:networks:addport',
+ args=[port.network_id])
+ res = self.client.post(url, form_data)
+
+ self.assertNoFormErrors(res)
+ redir_url = reverse('horizon:admin:networks:detail',
+ args=[port.network_id])
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('port_get',)})
+ def test_port_update_get(self):
+ port = self.ports.first()
+ api.quantum.port_get(IsA(http.HttpRequest),
+ port.id)\
+ .AndReturn(port)
+ self.mox.ReplayAll()
+
+ url = reverse('horizon:admin:networks:editport',
+ args=[port.network_id, port.id])
+ res = self.client.get(url)
+
+ self.assertTemplateUsed(res, 'admin/networks/ports/update.html')
+
+ @test.create_stubs({api.quantum: ('port_get',
+ 'port_modify')})
+ def test_port_update_post(self):
+ port = self.ports.first()
+ api.quantum.port_get(IsA(http.HttpRequest), port.id)\
+ .AndReturn(port)
+ api.quantum.port_modify(IsA(http.HttpRequest), port.id,
+ name=port.name, device_id=port.device_id)\
+ .AndReturn(port)
+ self.mox.ReplayAll()
+
+ formData = {'tenant_id': port.tenant_id,
+ 'network_id': port.network_id,
+ 'port_id': port.id,
+ 'name': port.name,
+ 'device_id': port.device_id}
+ url = reverse('horizon:admin:networks:editport',
+ args=[port.network_id, port.id])
+ res = self.client.post(url, formData)
+
+ redir_url = reverse('horizon:admin:networks:detail',
+ args=[port.network_id])
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('port_get',
+ 'port_modify')})
+ def test_port_update_post_exception(self):
+ port = self.ports.first()
+ api.quantum.port_get(IsA(http.HttpRequest), port.id)\
+ .AndReturn(port)
+ api.quantum.port_modify(IsA(http.HttpRequest), port.id,
+ name=port.name, device_id=port.device_id)\
+ .AndRaise(self.exceptions.quantum)
+ self.mox.ReplayAll()
+
+ formData = {'tenant_id': port.tenant_id,
+ 'network_id': port.network_id,
+ 'port_id': port.id,
+ 'name': port.name,
+ 'device_id': port.device_id}
+ url = reverse('horizon:admin:networks:editport',
+ args=[port.network_id, port.id])
+ res = self.client.post(url, formData)
+
+ redir_url = reverse('horizon:admin:networks:detail',
+ args=[port.network_id])
+ self.assertRedirectsNoFollow(res, redir_url)
+
+ @test.create_stubs({api.quantum: ('port_delete',
+ 'subnet_list',
+ 'port_list',)})
+ def test_port_delete(self):
+ port = self.ports.first()
+ network_id = port.network_id
+ api.quantum.port_delete(IsA(http.HttpRequest), port.id)
+ api.quantum.subnet_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.subnets.first()])
+ api.quantum.port_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.ports.first()])
+ self.mox.ReplayAll()
+
+ formData = {'action': 'ports__delete__%s' % port.id}
+ url = reverse('horizon:admin:networks:detail',
+ args=[network_id])
+ res = self.client.post(url, formData)
+
+ self.assertRedirectsNoFollow(res, url)
+
+ @test.create_stubs({api.quantum: ('port_delete',
+ 'subnet_list',
+ 'port_list',)})
+ def test_port_delete_exception(self):
+ port = self.ports.first()
+ network_id = port.network_id
+ api.quantum.port_delete(IsA(http.HttpRequest), port.id)\
+ .AndRaise(self.exceptions.quantum)
+ api.quantum.subnet_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.subnets.first()])
+ api.quantum.port_list(IsA(http.HttpRequest), network_id=network_id)\
+ .AndReturn([self.ports.first()])
+ self.mox.ReplayAll()
+
+ formData = {'action': 'ports__delete__%s' % port.id}
+ url = reverse('horizon:admin:networks:detail',
+ args=[network_id])
+ res = self.client.post(url, formData)
+
+ self.assertRedirectsNoFollow(res, url)
diff --git a/openstack_dashboard/dashboards/admin/networks/urls.py b/openstack_dashboard/dashboards/admin/networks/urls.py
new file mode 100644
index 000000000..59387d886
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/urls.py
@@ -0,0 +1,47 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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, include
+from .views import IndexView, CreateView, DetailView, UpdateView
+
+from .subnets.views import CreateView as AddSubnetView
+from .subnets.views import UpdateView as EditSubnetView
+from .ports.views import CreateView as AddPortView
+from .ports.views import UpdateView as EditPortView
+
+from .subnets import urls as subnet_urls
+from .ports import urls as port_urls
+
+
+NETWORKS = r'^(?P<network_id>[^/]+)/%s$'
+
+
+urlpatterns = patterns('',
+ url(r'^$', IndexView.as_view(), name='index'),
+ url(r'^create/$', CreateView.as_view(), name='create'),
+ url(NETWORKS % 'update', UpdateView.as_view(), name='update'),
+ # for detail view
+ url(NETWORKS % 'detail', DetailView.as_view(), name='detail'),
+ url(NETWORKS % 'subnets/create', AddSubnetView.as_view(),
+ name='addsubnet'),
+ url(NETWORKS % 'ports/create', AddPortView.as_view(), name='addport'),
+ url(r'^(?P<network_id>[^/]+)/subnets/(?P<subnet_id>[^/]+)/update$',
+ EditSubnetView.as_view(), name='editsubnet'),
+ url(r'^(?P<network_id>[^/]+)/ports/(?P<port_id>[^/]+)/update$',
+ EditPortView.as_view(), name='editport'),
+
+ url(r'^subnets/', include(subnet_urls, namespace='subnets')),
+ url(r'^ports/', include(port_urls, namespace='ports')))
diff --git a/openstack_dashboard/dashboards/admin/networks/views.py b/openstack_dashboard/dashboards/admin/networks/views.py
new file mode 100644
index 000000000..2c38adb8e
--- /dev/null
+++ b/openstack_dashboard/dashboards/admin/networks/views.py
@@ -0,0 +1,140 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 NEC Corporation
+#
+# 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.core.urlresolvers import reverse_lazy
+from django.utils.translation import ugettext_lazy as _
+from django.utils.datastructures import SortedDict
+
+from horizon import exceptions
+from horizon import forms
+from horizon import tables
+
+from openstack_dashboard import api
+from openstack_dashboard.dashboards.project.networks import views as user_views
+from .tables import NetworksTable
+from .subnets.tables import SubnetsTable
+from .ports.tables import PortsTable
+from .forms import CreateNetwork, UpdateNetwork
+
+
+LOG = logging.getLogger(__name__)
+
+
+class IndexView(tables.DataTableView):
+ table_class = NetworksTable
+ template_name = 'admin/networks/index.html'
+
+ def _get_tenant_list(self):
+ if not hasattr(self, "_tenants"):
+ try:
+ tenants = api.keystone.tenant_list(self.request, admin=True)
+ except:
+ tenants = []
+ msg = _('Unable to retrieve instance tenant information.')
+ exceptions.handle(self.request, msg)
+
+ tenant_dict = SortedDict([(t.id, t) for t in tenants])
+ self._tenants = tenant_dict
+ return self._tenants
+
+ def get_data(self):
+ try:
+ networks = api.quantum.network_list(self.request)
+ except:
+ networks = []
+ msg = _('Network list can not be retrieved.')
+ exceptions.handle(self.request, msg)
+ if networks:
+ tenant_dict = self._get_tenant_list()
+ for n in networks:
+ # Set tenant name
+ tenant = tenant_dict.get(n.tenant_id, None)
+ n.tenant_name = getattr(tenant, 'name', None)
+ # If name is empty use UUID as name
+ n.set_id_as_name_if_empty()
+ return networks
+
+
+class CreateView(forms.ModalFormView):
+ form_class = CreateNetwork
+ template_name = 'admin/networks/create.html'
+ success_url = reverse_lazy('horizon:admin:networks:index')
+
+
+class DetailView(tables.MultiTableView):
+ table_classes = (SubnetsTable, PortsTable)
+ template_name = 'project/networks/detail.html'
+ failure_url = reverse_lazy('horizon:admin:networks:index')
+
+ def get_subnets_data(self):
+ try:
+ network_id = self.kwargs['network_id']
+ subnets = api.quantum.subnet_list(self.request,
+ network_id=network_id)
+ except:
+ subnets = []
+ msg = _('Subnet list can not be retrieved.')
+ exceptions.handle(self.request, msg)
+ for s in subnets:
+ s.set_id_as_name_if_empty()
+ return subnets
+
+ def get_ports_data(self):
+ try:
+ network_id = self.kwargs['network_id']
+ ports = api.quantum.port_list(self.request, network_id=network_id)
+ except:
+ ports = []
+ msg = _('Port list can not be retrieved.')
+ exceptions.handle(self.request, msg)
+ for p in ports:
+ p.set_id_as_name_if_empty()
+ return ports
+
+ def _get_data(self):
+ if not hasattr(self, "_network"):
+ try:
+ network_id = self.kwargs['network_id']
+ network = api.quantum.network_get(self.request, network_id)
+ network.set_id_as_name_if_empty(length=0)
+ except:
+ redirect = self.failure_url
+ exceptions.handle(self.request,
+ _('Unable to retrieve details for '
+ 'network "%s".') % network_id,
+ redirect=redirect)
+ self._network = network
+ return self._network
+
+ def get_context_data(self, **kwargs):
+ context = super(DetailView, self).get_context_data(**kwargs)
+ context["network"] = self._get_data()
+ return context
+
+
+class UpdateView(user_views.UpdateView):
+ form_class = UpdateNetwork
+ template_name = 'admin/networks/update.html'
+ success_url = reverse_lazy('horizon:admin:networks:index')
+
+ def get_initial(self):
+ network = self._get_object()
+ return {'network_id': network['id'],
+ 'tenant_id': network['tenant_id'],
+ 'name': network['name'],
+ 'shared': network['shared']}