diff options
author | Gabriel Hurley <gabriel@strikeawe.com> | 2012-10-04 15:43:40 -0700 |
---|---|---|
committer | Gabriel Hurley <gabriel@strikeawe.com> | 2012-10-11 11:47:50 -0700 |
commit | cb8e7c1f8f0b238b88253cd6d82092cbe530ba9e (patch) | |
tree | 9ee3463e05ae6cf2f9cee5309a648538471c02b9 /openstack_dashboard/dashboards/admin/networks | |
parent | ef1e1d9b7a1fd4140518318ead8b7174a6a434ab (diff) | |
download | horizon-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')
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']} |