Merge "Allow users to create a rich network topology"

This commit is contained in:
Jenkins
2013-04-08 10:11:30 +00:00
committed by Gerrit Code Review
5 changed files with 159 additions and 18 deletions

View File

@@ -412,7 +412,8 @@ def router_add_interface(request, router_id, subnet_id=None, port_id=None):
body['subnet_id'] = subnet_id body['subnet_id'] = subnet_id
if port_id: if port_id:
body['port_id'] = port_id body['port_id'] = port_id
quantumclient(request).add_interface_router(router_id, body) client = quantumclient(request)
return client.add_interface_router(router_id, body)
def router_remove_interface(request, router_id, subnet_id=None, port_id=None): def router_remove_interface(request, router_id, subnet_id=None, port_id=None):

View File

@@ -22,13 +22,19 @@ from django.utils.translation import ugettext_lazy as _
from horizon import forms from horizon import forms
from horizon import messages from horizon import messages
from horizon import exceptions from horizon import exceptions
from horizon.utils import fields
from openstack_dashboard import api from openstack_dashboard import api
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
class AddInterface(forms.SelfHandlingForm): class AddInterface(forms.SelfHandlingForm):
subnet_id = forms.ChoiceField(label=_("Subnet"), required=False) subnet_id = forms.ChoiceField(label=_("Subnet"))
ip_address = fields.IPField(
label=_("IP Address (optional)"), required=False, initial="",
help_text=_("You can specify an IP address of the interface "
"created if you want (e.g. 192.168.0.254)."),
version=fields.IPv4 | fields.IPv6, mask=False)
router_name = forms.CharField(label=_("Router Name"), router_name = forms.CharField(label=_("Router Name"),
widget=forms.TextInput( widget=forms.TextInput(
attrs={'readonly': 'readonly'})) attrs={'readonly': 'readonly'}))
@@ -70,24 +76,73 @@ class AddInterface(forms.SelfHandlingForm):
return choices return choices
def handle(self, request, data): def handle(self, request, data):
if data['ip_address']:
port = self._add_interface_by_port(request, data)
else:
port = self._add_interface_by_subnet(request, data)
msg = _('Interface added')
if port:
msg += ' ' + port.fixed_ips[0]['ip_address']
LOG.debug(msg)
messages.success(request, msg)
return True
def _add_interface_by_subnet(self, request, data):
router_id = data['router_id']
try: try:
api.quantum.router_add_interface(request, router_inf = api.quantum.router_add_interface(
data['router_id'], request, router_id, subnet_id=data['subnet_id'])
subnet_id=data['subnet_id'])
msg = _('Interface added')
LOG.debug(msg)
messages.success(request, msg)
return True
except Exception as e: except Exception as e:
msg = _('Failed to add_interface %s') % e.message self._handle_error(request, router_id, e)
try:
port = api.quantum.port_get(request, router_inf['port_id'])
except:
# Ignore an error when port_get() since it is just
# to get an IP address for the interface.
port = None
return port
def _add_interface_by_port(self, request, data):
router_id = data['router_id']
subnet_id = data['subnet_id']
try:
subnet = api.quantum.subnet_get(request, subnet_id)
except:
msg = _('Unable to get subnet "%s"') % subnet_id
self._handle_error(request, router_id, msg)
try:
ip_address = data['ip_address']
body = {'network_id': subnet.network_id,
'fixed_ips': [{'subnet_id': subnet.id,
'ip_address': ip_address}]}
port = api.quantum.port_create(request, **body)
except Exception as e:
self._handle_error(request, router_id, e)
try:
api.quantum.router_add_interface(request, router_id,
port_id=port.id)
except Exception as e:
self._delete_port(request, port)
self._handle_error(request, router_id, e)
return port
def _handle_error(self, request, router_id, reason):
msg = _('Failed to add_interface: %s') % reason
LOG.info(msg)
redirect = reverse(self.failure_url, args=[router_id])
exceptions.handle(request, msg, redirect=redirect)
def _delete_port(self, request, port):
try:
api.quantum.port_delete(request, port.id)
except:
msg = _('Failed to delete port %s') % port.id
LOG.info(msg) LOG.info(msg)
messages.error(request, msg) exceptions.handle(request, msg)
redirect = reverse(self.failure_url, args=[data['router_id']])
exceptions.handle(request, msg, redirect=redirect)
class SetGatewayForm(forms.SelfHandlingForm): class SetGatewayForm(forms.SelfHandlingForm):
network_id = forms.ChoiceField(label=_("External Network"), required=False) network_id = forms.ChoiceField(label=_("External Network"))
router_name = forms.CharField(label=_("Router Name"), router_name = forms.CharField(label=_("Router Name"),
widget=forms.TextInput( widget=forms.TextInput(
attrs={'readonly': 'readonly'})) attrs={'readonly': 'readonly'}))
@@ -132,6 +187,5 @@ class SetGatewayForm(forms.SelfHandlingForm):
except Exception as e: except Exception as e:
msg = _('Failed to set gateway %s') % e.message msg = _('Failed to set gateway %s') % e.message
LOG.info(msg) LOG.info(msg)
messages.error(request, msg)
redirect = reverse(self.failure_url) redirect = reverse(self.failure_url)
exceptions.handle(request, msg, redirect=redirect) exceptions.handle(request, msg, redirect=redirect)

View File

@@ -71,6 +71,11 @@ class RemoveInterface(tables.DeleteAction):
args=[router_id]) args=[router_id])
exceptions.handle(request, msg, redirect=redirect) exceptions.handle(request, msg, redirect=redirect)
def allowed(self, request, datum=None):
if datum and datum['device_owner'] == 'network:router_gateway':
return False
return True
class PortsTable(tables.DataTable): class PortsTable(tables.DataTable):
name = tables.Column("name", name = tables.Column("name",

View File

@@ -15,7 +15,12 @@
</div> </div>
<div class="right"> <div class="right">
<h3>{% trans "Description" %}:</h3> <h3>{% trans "Description" %}:</h3>
<p>{% trans "You can connect a specified subnet to the router." %}</p> <p>
{% trans "You can connect a specified subnet to the router." %}
</p>
<p>
{% trans "The default IP address of the interface created is a gateway of the selected subnet. You can specify another IP address of the interface here. You must select a subnet to which the specified IP address belongs to from the above list." %}
</p>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -150,12 +150,21 @@ class RouterActionTests(test.TestCase):
def _test_router_addinterface(self, raise_error=False): def _test_router_addinterface(self, raise_error=False):
router = self.routers.first() router = self.routers.first()
subnet = self.subnets.first() subnet = self.subnets.first()
port = self.ports.first()
add_interface = api.quantum.router_add_interface( add_interface = api.quantum.router_add_interface(
IsA(http.HttpRequest), router.id, subnet_id=subnet.id) IsA(http.HttpRequest), router.id, subnet_id=subnet.id)
if raise_error: if raise_error:
add_interface.AndRaise(self.exceptions.quantum) add_interface.AndRaise(self.exceptions.quantum)
else: else:
add_interface.AndReturn(None) add_interface.AndReturn({'subnet_id': subnet.id,
'port_id': port.id})
api.quantum.port_get(IsA(http.HttpRequest), port.id)\
.AndReturn(port)
self._check_router_addinterface(router, subnet)
def _check_router_addinterface(self, router, subnet, ip_address=''):
# mock APIs used to show router detail
api.quantum.router_get(IsA(http.HttpRequest), router.id)\ api.quantum.router_get(IsA(http.HttpRequest), router.id)\
.AndReturn(router) .AndReturn(router)
self._mock_network_list(router['tenant_id']) self._mock_network_list(router['tenant_id'])
@@ -163,7 +172,8 @@ class RouterActionTests(test.TestCase):
form_data = {'router_id': router.id, form_data = {'router_id': router.id,
'router_name': router.name, 'router_name': router.name,
'subnet_id': subnet.id} 'subnet_id': subnet.id,
'ip_address': ip_address}
url = reverse('horizon:%s:routers:addinterface' % self.DASHBOARD, url = reverse('horizon:%s:routers:addinterface' % self.DASHBOARD,
args=[router.id]) args=[router.id])
@@ -174,6 +184,7 @@ class RouterActionTests(test.TestCase):
@test.create_stubs({api.quantum: ('router_get', @test.create_stubs({api.quantum: ('router_get',
'router_add_interface', 'router_add_interface',
'port_get',
'network_list')}) 'network_list')})
def test_router_addinterface(self): def test_router_addinterface(self):
self._test_router_addinterface() self._test_router_addinterface()
@@ -184,6 +195,71 @@ class RouterActionTests(test.TestCase):
def test_router_addinterface_exception(self): def test_router_addinterface_exception(self):
self._test_router_addinterface(raise_error=True) self._test_router_addinterface(raise_error=True)
def _test_router_addinterface_ip_addr(self, errors=[]):
router = self.routers.first()
subnet = self.subnets.first()
port = self.ports.first()
ip_addr = port['fixed_ips'][0]['ip_address']
self._setup_mock_addinterface_ip_addr(router, subnet, port,
ip_addr, errors)
self._check_router_addinterface(router, subnet, ip_addr)
def _setup_mock_addinterface_ip_addr(self, router, subnet, port,
ip_addr, errors=[]):
subnet_get = api.quantum.subnet_get(IsA(http.HttpRequest), subnet.id)
if 'subnet_get' in errors:
subnet_get.AndRaise(self.exceptions.quantum)
return
subnet_get.AndReturn(subnet)
params = {'network_id': subnet.network_id,
'fixed_ips': [{'subnet_id': subnet.id,
'ip_address': ip_addr}]}
port_create = api.quantum.port_create(IsA(http.HttpRequest), **params)
if 'port_create' in errors:
port_create.AndRaise(self.exceptions.quantum)
return
port_create.AndReturn(port)
add_inf = api.quantum.router_add_interface(
IsA(http.HttpRequest), router.id, port_id=port.id)
if 'add_interface' not in errors:
return
add_inf.AndRaise(self.exceptions.quantum)
port_delete = api.quantum.port_delete(IsA(http.HttpRequest), port.id)
if 'port_delete' in errors:
port_delete.AndRaise(self.exceptions.quantum)
@test.create_stubs({api.quantum: ('router_add_interface', 'subnet_get',
'port_create',
'router_get', 'network_list')})
def test_router_addinterface_ip_addr(self):
self._test_router_addinterface_ip_addr()
@test.create_stubs({api.quantum: ('subnet_get',
'router_get', 'network_list')})
def test_router_addinterface_ip_addr_exception_subnet_get(self):
self._test_router_addinterface_ip_addr(errors=['subnet_get'])
@test.create_stubs({api.quantum: ('subnet_get', 'port_create',
'router_get', 'network_list')})
def test_router_addinterface_ip_addr_exception_port_create(self):
self._test_router_addinterface_ip_addr(errors=['port_create'])
@test.create_stubs({api.quantum: ('router_add_interface', 'subnet_get',
'port_create', 'port_delete',
'router_get', 'network_list')})
def test_router_addinterface_ip_addr_exception_add_interface(self):
self._test_router_addinterface_ip_addr(errors=['add_interface'])
@test.create_stubs({api.quantum: ('router_add_interface', 'subnet_get',
'port_create', 'port_delete',
'router_get', 'network_list')})
def test_router_addinterface_ip_addr_exception_port_delete(self):
self._test_router_addinterface_ip_addr(errors=['add_interface',
'port_delete'])
@test.create_stubs({api.quantum: ('router_get', @test.create_stubs({api.quantum: ('router_get',
'router_add_gateway', 'router_add_gateway',
'network_list')}) 'network_list')})