Merge "Allow users to create a rich network topology"
This commit is contained in:
@@ -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):
|
||||||
|
@@ -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)
|
||||||
|
@@ -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",
|
||||||
|
@@ -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 %}
|
||||||
|
|
||||||
|
@@ -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')})
|
||||||
|
Reference in New Issue
Block a user