Merge "Adds initial SRIOV creation/config support"
This commit is contained in:
commit
a174b4294d
@ -678,6 +678,7 @@ Default::
|
||||
'enable_vpn': True,
|
||||
'profile_support': None,
|
||||
'supported_provider_types': ["*"],
|
||||
'supported_vnic_types': ["*"],
|
||||
'segmentation_id_range': {}
|
||||
}
|
||||
|
||||
@ -803,6 +804,19 @@ be available to choose from.
|
||||
|
||||
Example: ``['local', 'flat', 'gre']``
|
||||
|
||||
``supported_vnic_types``:
|
||||
|
||||
.. versionadded:: 2015.1(Kilo)
|
||||
|
||||
Default ``['*']``
|
||||
|
||||
For use with the port binding extension. Use this to explicitly set which VNIC
|
||||
types are supported; only those listed will be shown when creating or editing
|
||||
a port. VNIC types include normal, direct and macvtap. By default all VNIC
|
||||
types will be available to choose from.
|
||||
|
||||
Example ``['normal', 'direct']``
|
||||
|
||||
``segmentation_id_range``:
|
||||
|
||||
.. versionadded:: 2014.2(Juno)
|
||||
|
@ -88,9 +88,9 @@ class Network(NeutronAPIDictWrapper):
|
||||
def __init__(self, apiresource):
|
||||
apiresource['admin_state'] = \
|
||||
'UP' if apiresource['admin_state_up'] else 'DOWN'
|
||||
# Django cannot handle a key name with a colon, so remap another key
|
||||
# Django cannot handle a key name with ':', so use '__'
|
||||
for key in apiresource.keys():
|
||||
if key.find(':'):
|
||||
if ':' in key:
|
||||
apiresource['__'.join(key.split(':'))] = apiresource[key]
|
||||
super(Network, self).__init__(apiresource)
|
||||
|
||||
@ -112,6 +112,10 @@ class Port(NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron ports."""
|
||||
|
||||
def __init__(self, apiresource):
|
||||
# Django cannot handle a key name with ':', so use '__'
|
||||
for key in apiresource.keys():
|
||||
if ':' in key:
|
||||
apiresource['__'.join(key.split(':'))] = apiresource[key]
|
||||
apiresource['admin_state'] = \
|
||||
'UP' if apiresource['admin_state_up'] else 'DOWN'
|
||||
if 'mac_learning_enabled' in apiresource:
|
||||
@ -729,6 +733,13 @@ def port_get(request, port_id, **params):
|
||||
return Port(port)
|
||||
|
||||
|
||||
def unescape_port_kwargs(**kwargs):
|
||||
for key in kwargs:
|
||||
if '__' in key:
|
||||
kwargs[':'.join(key.split('__'))] = kwargs.pop(key)
|
||||
return kwargs
|
||||
|
||||
|
||||
def port_create(request, network_id, **kwargs):
|
||||
"""Create a port on a specified network.
|
||||
|
||||
@ -743,6 +754,7 @@ def port_create(request, network_id, **kwargs):
|
||||
# In the case policy profiles are being used, profile id is needed.
|
||||
if 'policy_profile_id' in kwargs:
|
||||
kwargs['n1kv:profile_id'] = kwargs.pop('policy_profile_id')
|
||||
kwargs = unescape_port_kwargs(**kwargs)
|
||||
body = {'port': {'network_id': network_id}}
|
||||
if 'tenant_id' not in kwargs:
|
||||
kwargs['tenant_id'] = request.user.project_id
|
||||
@ -758,6 +770,7 @@ def port_delete(request, port_id):
|
||||
|
||||
def port_update(request, port_id, **kwargs):
|
||||
LOG.debug("port_update(): portid=%s, kwargs=%s" % (port_id, kwargs))
|
||||
kwargs = unescape_port_kwargs(**kwargs)
|
||||
body = {'port': kwargs}
|
||||
port = neutronclient(request).update_port(port_id, body=body).get('port')
|
||||
return Port(port)
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
@ -27,6 +28,8 @@ from openstack_dashboard.dashboards.project.networks.ports \
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
VNIC_TYPES = [('normal', _('Normal')), ('direct', _('Direct')),
|
||||
('macvtap', _('MacVTap'))]
|
||||
|
||||
|
||||
class CreatePort(forms.SelfHandlingForm):
|
||||
@ -49,9 +52,36 @@ class CreatePort(forms.SelfHandlingForm):
|
||||
help_text=_("Device owner attached to the "
|
||||
"port"),
|
||||
required=False)
|
||||
binding__host_id = forms.CharField(
|
||||
label=_("Binding: Host"),
|
||||
help_text=_("The ID of the host where the port is allocated. In some "
|
||||
"cases, different implementations can run on different "
|
||||
"hosts."),
|
||||
required=False)
|
||||
|
||||
failure_url = 'horizon:admin:networks:detail'
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(CreatePort, self).__init__(request, *args, **kwargs)
|
||||
if api.neutron.is_extension_supported(request, 'binding'):
|
||||
neutron_settings = getattr(settings,
|
||||
'OPENSTACK_NEUTRON_NETWORK', {})
|
||||
supported_vnic_types = neutron_settings.get(
|
||||
'supported_vnic_types', ['*'])
|
||||
if supported_vnic_types == ['*']:
|
||||
vnic_type_choices = VNIC_TYPES
|
||||
else:
|
||||
vnic_type_choices = [
|
||||
vnic_type for vnic_type in VNIC_TYPES
|
||||
if vnic_type[0] in supported_vnic_types
|
||||
]
|
||||
|
||||
self.fields['binding__vnic_type'] = forms.ChoiceField(
|
||||
choices=vnic_type_choices,
|
||||
label=_("Binding: VNIC Type"),
|
||||
help_text=_("The VNIC type that is bound to the neutron port"),
|
||||
required=False)
|
||||
|
||||
if api.neutron.is_extension_supported(request, 'mac-learning'):
|
||||
self.fields['mac_state'] = forms.BooleanField(
|
||||
label=_("MAC Learning State"), initial=False, required=False)
|
||||
@ -78,7 +108,7 @@ class CreatePort(forms.SelfHandlingForm):
|
||||
msg = _('Failed to create a port for network %s') \
|
||||
% data['network_id']
|
||||
LOG.info(msg)
|
||||
redirect = reverse('horizon:admin:networks:detail',
|
||||
redirect = reverse(self.failure_url,
|
||||
args=(data['network_id'],))
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
|
||||
@ -92,6 +122,13 @@ class UpdatePort(project_forms.UpdatePort):
|
||||
help_text=_("Device owner attached to the "
|
||||
"port"),
|
||||
required=False)
|
||||
binding__host_id = forms.CharField(
|
||||
label=_("Binding: Host"),
|
||||
help_text=_("The ID of the host where the port is allocated. In some "
|
||||
"cases, different implementations can run on different "
|
||||
"hosts."),
|
||||
required=False)
|
||||
|
||||
failure_url = 'horizon:admin:networks:detail'
|
||||
|
||||
def handle(self, request, data):
|
||||
@ -99,13 +136,21 @@ class UpdatePort(project_forms.UpdatePort):
|
||||
LOG.debug('params = %s' % data)
|
||||
extension_kwargs = {}
|
||||
data['admin_state'] = (data['admin_state'] == 'True')
|
||||
if 'binding__vnic_type' in data:
|
||||
extension_kwargs['binding__vnic_type'] = \
|
||||
data['binding__vnic_type']
|
||||
|
||||
if 'mac_state' in data:
|
||||
extension_kwargs['mac_learning_enabled'] = data['mac_state']
|
||||
port = api.neutron.port_update(request, data['port_id'],
|
||||
|
||||
port = api.neutron.port_update(request,
|
||||
data['port_id'],
|
||||
name=data['name'],
|
||||
admin_state_up=data['admin_state'],
|
||||
device_id=data['device_id'],
|
||||
device_owner=data['device_owner'],
|
||||
binding__host_id=data
|
||||
['binding__host_id'],
|
||||
**extension_kwargs)
|
||||
msg = _('Port %s was successfully updated.') % data['port_id']
|
||||
LOG.debug(msg)
|
||||
|
@ -73,36 +73,14 @@ class CreatePort(tables.LinkAction):
|
||||
return reverse(self.url, args=(network_id,))
|
||||
|
||||
|
||||
class UpdatePort(policy.PolicyTargetMixin, tables.LinkAction):
|
||||
name = "update"
|
||||
verbose_name = _("Edit Port")
|
||||
class UpdatePort(project_tables.UpdatePort):
|
||||
url = "horizon:admin:networks:editport"
|
||||
classes = ("ajax-modal",)
|
||||
icon = "pencil"
|
||||
policy_rules = (("network", "update_port"),)
|
||||
|
||||
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):
|
||||
class PortsTable(project_tables.PortsTable):
|
||||
name = tables.Column("name_or_id",
|
||||
verbose_name=_("Name"),
|
||||
link="horizon:admin:networks:ports:detail")
|
||||
fixed_ips = tables.Column(
|
||||
project_tables.get_fixed_ips, verbose_name=_("Fixed IPs"))
|
||||
device_id = tables.Column(
|
||||
project_tables.get_attached, verbose_name=_("Device Attached"))
|
||||
status = tables.Column(
|
||||
"status",
|
||||
verbose_name=_("Status"),
|
||||
display_choices=project_tables.STATUS_DISPLAY_CHOICES)
|
||||
admin_state = tables.Column("admin_state",
|
||||
verbose_name=_("Admin State"),
|
||||
display_choices=project_tables.DISPLAY_CHOICES)
|
||||
mac_state = tables.Column("mac_state", empty_value=api.neutron.OFF_STATE,
|
||||
verbose_name=_("Mac Learning State"))
|
||||
|
||||
class Meta(object):
|
||||
name = "ports"
|
||||
@ -110,10 +88,3 @@ class PortsTable(tables.DataTable):
|
||||
table_actions = (CreatePort, DeletePort)
|
||||
row_actions = (UpdatePort, DeletePort,)
|
||||
hidden_title = False
|
||||
|
||||
def __init__(self, request, data=None, needs_form_wrapper=None, **kwargs):
|
||||
super(PortsTable, self).__init__(request, data=data,
|
||||
needs_form_wrapper=needs_form_wrapper,
|
||||
**kwargs)
|
||||
if not api.neutron.is_extension_supported(request, 'mac-learning'):
|
||||
del self.columns['mac_state']
|
||||
|
@ -12,31 +12,13 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
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
|
||||
from openstack_dashboard.dashboards.project.networks.ports \
|
||||
import tabs as project_tabs
|
||||
|
||||
|
||||
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.neutron.port_get(self.request, port_id)
|
||||
except Exception:
|
||||
redirect = reverse('horizon:admin:networks:index')
|
||||
msg = _('Unable to retrieve port details.')
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
return {'port': port}
|
||||
class OverviewTab(project_tabs.OverviewTab):
|
||||
template_name = "admin/networks/ports/_detail_overview.html"
|
||||
|
||||
|
||||
class PortDetailTabs(tabs.TabGroup):
|
||||
slug = "port_details"
|
||||
class PortDetailTabs(project_tabs.PortDetailTabs):
|
||||
tabs = (OverviewTab,)
|
||||
|
@ -15,7 +15,7 @@
|
||||
from django.conf.urls import patterns
|
||||
from django.conf.urls import url
|
||||
|
||||
from openstack_dashboard.dashboards.project.networks.ports import views
|
||||
from openstack_dashboard.dashboards.admin.networks.ports import views
|
||||
|
||||
PORTS = r'^(?P<port_id>[^/]+)/%s$'
|
||||
VIEW_MOD = 'openstack_dashboard.dashboards.admin.networks.ports.views'
|
||||
|
@ -20,26 +20,29 @@ from horizon import forms
|
||||
from horizon.utils import memoized
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from openstack_dashboard.dashboards.admin.networks.ports \
|
||||
import forms as ports_forms
|
||||
from openstack_dashboard.dashboards.admin.networks.ports \
|
||||
import tables as ports_tables
|
||||
from openstack_dashboard.dashboards.admin.networks.ports \
|
||||
import tabs as ports_tabs
|
||||
from openstack_dashboard.dashboards.project.networks.ports \
|
||||
import views as project_views
|
||||
|
||||
from openstack_dashboard.dashboards.admin.networks.ports \
|
||||
import forms as project_forms
|
||||
|
||||
|
||||
class CreateView(forms.ModalFormView):
|
||||
form_class = project_forms.CreatePort
|
||||
form_class = ports_forms.CreatePort
|
||||
form_id = "create_port_form"
|
||||
modal_header = _("Create Port")
|
||||
template_name = 'admin/networks/ports/create.html'
|
||||
submit_label = _("Create Port")
|
||||
submit_url = "horizon:admin:networks:addport"
|
||||
success_url = 'horizon:admin:networks:detail'
|
||||
failure_url = 'horizon:admin:networks:detail'
|
||||
page_title = _("Create Port")
|
||||
template_name = 'admin/networks/ports/create.html'
|
||||
url = 'horizon:admin:networks:detail'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse(self.success_url,
|
||||
return reverse(self.url,
|
||||
args=(self.kwargs['network_id'],))
|
||||
|
||||
@memoized.memoized_method
|
||||
@ -48,7 +51,7 @@ class CreateView(forms.ModalFormView):
|
||||
network_id = self.kwargs["network_id"]
|
||||
return api.neutron.network_get(self.request, network_id)
|
||||
except Exception:
|
||||
redirect = reverse(self.failure_url,
|
||||
redirect = reverse(self.url,
|
||||
args=(self.kwargs['network_id'],))
|
||||
msg = _("Unable to retrieve network.")
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
@ -66,9 +69,32 @@ class CreateView(forms.ModalFormView):
|
||||
"network_name": network.name}
|
||||
|
||||
|
||||
class DetailView(project_views.DetailView):
|
||||
tab_group_class = ports_tabs.PortDetailTabs
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(DetailView, self).get_context_data(**kwargs)
|
||||
port = context["port"]
|
||||
table = ports_tables.PortsTable(self.request,
|
||||
network_id=port.network_id)
|
||||
context["url"] = reverse('horizon:admin:networks:index')
|
||||
context["actions"] = table.render_row_actions(port)
|
||||
return context
|
||||
|
||||
@staticmethod
|
||||
def get_redirect_url():
|
||||
return reverse('horizon:admin:networks:index')
|
||||
|
||||
|
||||
class UpdateView(project_views.UpdateView):
|
||||
form_class = project_forms.UpdatePort
|
||||
form_class = ports_forms.UpdatePort
|
||||
template_name = 'admin/networks/ports/update.html'
|
||||
context_object_name = 'port'
|
||||
submit_url = "horizon:admin:networks:editport"
|
||||
success_url = 'horizon:admin:networks:detail'
|
||||
|
||||
def get_initial(self):
|
||||
initial = super(UpdateView, self).get_initial()
|
||||
port = self._get_object()
|
||||
initial['binding__host_id'] = port['binding__host_id']
|
||||
return initial
|
||||
|
@ -0,0 +1,73 @@
|
||||
{% load i18n sizeformat %}
|
||||
{% load url from future %}
|
||||
|
||||
<div class="detail">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ port.name|default:_("None") }}</dd>
|
||||
<dt>{% trans "ID" %}</dt>
|
||||
<dd>{{ port.id|default:_("None") }}</dd>
|
||||
{% url 'horizon:project:networks:detail' port.network_id as network_url %}
|
||||
<dt>{% trans "Network ID" %}</dt>
|
||||
<dd><a href="{{ network_url }}">{{ port.network_id|default:_("None") }}</a></dd>
|
||||
<dt>{% trans "Project ID" %}</dt>
|
||||
<dd>{{ port.tenant_id|default:_("-") }}</dd>
|
||||
<dt>{% trans "MAC Address" %}</dt>
|
||||
<dd>{{ port.mac_address|default:_("None") }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ port.status_label|default:_("None") }}</dd>
|
||||
<dt>{% trans "Admin State" %}</dt>
|
||||
<dd>{{ port.admin_state_label|default:_("None") }}</dd>
|
||||
{% if port.mac_state %}
|
||||
<dt>{% trans "MAC Learning State" %}</dt>
|
||||
<dd>{% trans "On" %}</dd>
|
||||
{% endif %}
|
||||
<h4>{% trans "Fixed IP" %}</h4>
|
||||
<hr class="header_rule">
|
||||
{% if port.fixed_ips.items|length > 1 %}
|
||||
{% for ip in port.fixed_ips %}
|
||||
<dt>{% trans "IP Address" %}</dt>
|
||||
<dd>{{ ip.ip_address }}</dd>
|
||||
{% url 'horizon:project:networks:subnets:detail' ip.subnet_id as subnet_url %}
|
||||
<dt>{% trans "Subnet ID" %}</dt>
|
||||
<dd><a href="{{ subnet_url }}">{{ ip.subnet_id }}</a></dd>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<dd>{% trans "None" %}</dd>
|
||||
{% endif %}
|
||||
<h4>{% trans "Attached Device" %}</h4>
|
||||
<hr class="header_rule">
|
||||
{% if port.device_id|length > 1 or port.device_owner %}
|
||||
<dt>{% trans "Device Owner" %}</dt>
|
||||
<dd>{{ port.device_owner|default:_("None") }}</dd>
|
||||
<dt>{% trans "Device ID" %}</dt>
|
||||
<dd>{{ port.device_id|default:_("None") }}</dd>
|
||||
{% else %}
|
||||
<dd>{% trans "No attached device" %}</dd>
|
||||
{% endif %}
|
||||
<h4>{% trans "Binding" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dt>{% trans "Host" %}</dt>
|
||||
<dd>{{ port.binding__host_id|default:_("None") }}</dd>
|
||||
<dt>{% trans "Profile" %}</dt>
|
||||
<dd>{{ port.binding__profile|default:_("None") }}</dd>
|
||||
<dt>{% trans "VIF Type" %}</dt>
|
||||
<dd>{{ port.binding__vif_type|replace_underscores }}</dd>
|
||||
<dt>{% trans "VIF Details" %}</dt>
|
||||
{% if port.binding__vif_details.items %}
|
||||
<dd>
|
||||
<ul>
|
||||
{% for key,value in port.binding__vif_details.items %}
|
||||
<li><b>{{ key }}</b> {{ value }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</dd>
|
||||
{% else %}
|
||||
<dd>{% trans "None" %}</dd>
|
||||
{% endif %}
|
||||
{% if port.binding__vnic_type %}
|
||||
<dt>{% trans "VNIC Type" %}</dt>
|
||||
<dd>{{ port.binding__vnic_type }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
@ -975,7 +975,9 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('horizon:admin:networks:ports:detail',
|
||||
@ -997,7 +999,7 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
|
||||
# admin DetailView is shared with userpanel one, so
|
||||
# redirection URL on error is userpanel index.
|
||||
redir_url = reverse('horizon:project:networks:index')
|
||||
redir_url = reverse('horizon:admin:networks:index')
|
||||
self.assertRedirectsNoFollow(res, redir_url)
|
||||
|
||||
@test.create_stubs({api.neutron: ('network_get',
|
||||
@ -1010,11 +1012,14 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
def test_port_create_get_with_mac_learning(self):
|
||||
self._test_port_create_get(mac_learning=True)
|
||||
|
||||
def _test_port_create_get(self, mac_learning=False):
|
||||
def _test_port_create_get(self, mac_learning=False, binding=False):
|
||||
network = self.networks.first()
|
||||
api.neutron.network_get(IsA(http.HttpRequest),
|
||||
network.id)\
|
||||
.AndReturn(self.networks.first())
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'binding')\
|
||||
.AndReturn(binding)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
@ -1036,9 +1041,9 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
'is_extension_supported',
|
||||
'port_create',)})
|
||||
def test_port_create_post_with_mac_learning(self):
|
||||
self._test_port_create_post(mac_learning=True)
|
||||
self._test_port_create_post(mac_learning=True, binding=False)
|
||||
|
||||
def _test_port_create_post(self, mac_learning=False):
|
||||
def _test_port_create_post(self, mac_learning=False, binding=False):
|
||||
network = self.networks.first()
|
||||
port = self.ports.first()
|
||||
api.neutron.network_get(IsA(http.HttpRequest),
|
||||
@ -1047,10 +1052,16 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
api.neutron.network_get(IsA(http.HttpRequest),
|
||||
network.id)\
|
||||
.AndReturn(self.networks.first())
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'binding')\
|
||||
.AndReturn(binding)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
extension_kwargs = {}
|
||||
if binding:
|
||||
extension_kwargs['binding__vnic_type'] = \
|
||||
port.binding__vnic_type
|
||||
if mac_learning:
|
||||
extension_kwargs['mac_learning_enabled'] = True
|
||||
api.neutron.port_create(IsA(http.HttpRequest),
|
||||
@ -1060,6 +1071,7 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
admin_state_up=port.admin_state_up,
|
||||
device_id=port.device_id,
|
||||
device_owner=port.device_owner,
|
||||
binding__host_id=port.binding__host_id,
|
||||
**extension_kwargs)\
|
||||
.AndReturn(port)
|
||||
self.mox.ReplayAll()
|
||||
@ -1069,7 +1081,10 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
'name': port.name,
|
||||
'admin_state': port.admin_state_up,
|
||||
'device_id': port.device_id,
|
||||
'device_owner': port.device_owner}
|
||||
'device_owner': port.device_owner,
|
||||
'binding__host_id': port.binding__host_id}
|
||||
if binding:
|
||||
form_data['binding__vnic_type'] = port.binding__vnic_type
|
||||
if mac_learning:
|
||||
form_data['mac_state'] = True
|
||||
url = reverse('horizon:admin:networks:addport',
|
||||
@ -1093,7 +1108,8 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
def test_port_create_post_exception_with_mac_learning(self):
|
||||
self._test_port_create_post_exception(mac_learning=True)
|
||||
|
||||
def _test_port_create_post_exception(self, mac_learning=False):
|
||||
def _test_port_create_post_exception(self, mac_learning=False,
|
||||
binding=False):
|
||||
network = self.networks.first()
|
||||
port = self.ports.first()
|
||||
api.neutron.network_get(IsA(http.HttpRequest),
|
||||
@ -1102,10 +1118,15 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
api.neutron.network_get(IsA(http.HttpRequest),
|
||||
network.id)\
|
||||
.AndReturn(self.networks.first())
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'binding')\
|
||||
.AndReturn(binding)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
extension_kwargs = {}
|
||||
if binding:
|
||||
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type
|
||||
if mac_learning:
|
||||
extension_kwargs['mac_learning_enabled'] = True
|
||||
api.neutron.port_create(IsA(http.HttpRequest),
|
||||
@ -1115,6 +1136,7 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
admin_state_up=port.admin_state_up,
|
||||
device_id=port.device_id,
|
||||
device_owner=port.device_owner,
|
||||
binding__host_id=port.binding__host_id,
|
||||
**extension_kwargs)\
|
||||
.AndRaise(self.exceptions.neutron)
|
||||
self.mox.ReplayAll()
|
||||
@ -1125,7 +1147,10 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
'admin_state': port.admin_state_up,
|
||||
'mac_state': True,
|
||||
'device_id': port.device_id,
|
||||
'device_owner': port.device_owner}
|
||||
'device_owner': port.device_owner,
|
||||
'binding__host_id': port.binding__host_id}
|
||||
if binding:
|
||||
form_data['binding__vnic_type'] = port.binding__vnic_type
|
||||
if mac_learning:
|
||||
form_data['mac_learning_enabled'] = True
|
||||
url = reverse('horizon:admin:networks:addport',
|
||||
@ -1147,11 +1172,14 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
def test_port_update_get_with_mac_learning(self):
|
||||
self._test_port_update_get(mac_learning=True)
|
||||
|
||||
def _test_port_update_get(self, mac_learning=False):
|
||||
def _test_port_update_get(self, mac_learning=False, binding=False):
|
||||
port = self.ports.first()
|
||||
api.neutron.port_get(IsA(http.HttpRequest),
|
||||
port.id)\
|
||||
.AndReturn(port)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'binding')\
|
||||
.AndReturn(binding)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
@ -1175,14 +1203,19 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
def test_port_update_post_with_mac_learning(self):
|
||||
self._test_port_update_post(mac_learning=True)
|
||||
|
||||
def _test_port_update_post(self, mac_learning=False):
|
||||
def _test_port_update_post(self, mac_learning=False, binding=False):
|
||||
port = self.ports.first()
|
||||
api.neutron.port_get(IsA(http.HttpRequest), port.id)\
|
||||
.AndReturn(port)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'binding')\
|
||||
.AndReturn(binding)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
extension_kwargs = {}
|
||||
if binding:
|
||||
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type
|
||||
if mac_learning:
|
||||
extension_kwargs['mac_learning_enabled'] = True
|
||||
api.neutron.port_update(IsA(http.HttpRequest), port.id,
|
||||
@ -1190,6 +1223,7 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
admin_state_up=port.admin_state_up,
|
||||
device_id=port.device_id,
|
||||
device_owner=port.device_owner,
|
||||
binding__host_id=port.binding__host_id,
|
||||
**extension_kwargs)\
|
||||
.AndReturn(port)
|
||||
self.mox.ReplayAll()
|
||||
@ -1199,7 +1233,10 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
'name': port.name,
|
||||
'admin_state': port.admin_state_up,
|
||||
'device_id': port.device_id,
|
||||
'device_owner': port.device_owner}
|
||||
'device_owner': port.device_owner,
|
||||
'binding__host_id': port.binding__host_id}
|
||||
if binding:
|
||||
form_data['binding__vnic_type'] = port.binding__vnic_type
|
||||
if mac_learning:
|
||||
form_data['mac_state'] = True
|
||||
url = reverse('horizon:admin:networks:editport',
|
||||
@ -1220,16 +1257,22 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
'is_extension_supported',
|
||||
'port_update')})
|
||||
def test_port_update_post_exception_with_mac_learning(self):
|
||||
self._test_port_update_post_exception(mac_learning=True)
|
||||
self._test_port_update_post_exception(mac_learning=True, binding=False)
|
||||
|
||||
def _test_port_update_post_exception(self, mac_learning=False):
|
||||
def _test_port_update_post_exception(self, mac_learning=False,
|
||||
binding=False):
|
||||
port = self.ports.first()
|
||||
api.neutron.port_get(IsA(http.HttpRequest), port.id)\
|
||||
.AndReturn(port)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'binding')\
|
||||
.AndReturn(binding)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
extension_kwargs = {}
|
||||
if binding:
|
||||
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type
|
||||
if mac_learning:
|
||||
extension_kwargs['mac_learning_enabled'] = True
|
||||
api.neutron.port_update(IsA(http.HttpRequest), port.id,
|
||||
@ -1237,6 +1280,7 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
admin_state_up=port.admin_state_up,
|
||||
device_id=port.device_id,
|
||||
device_owner=port.device_owner,
|
||||
binding__host_id=port.binding__host_id,
|
||||
**extension_kwargs)\
|
||||
.AndRaise(self.exceptions.neutron)
|
||||
self.mox.ReplayAll()
|
||||
@ -1246,7 +1290,10 @@ class NetworkPortTests(test.BaseAdminViewTests):
|
||||
'name': port.name,
|
||||
'admin_state': port.admin_state_up,
|
||||
'device_id': port.device_id,
|
||||
'device_owner': port.device_owner}
|
||||
'device_owner': port.device_owner,
|
||||
'binding__host_id': port.binding__host_id}
|
||||
if binding:
|
||||
form_data['binding__vnic_type'] = port.binding__vnic_type
|
||||
if mac_learning:
|
||||
form_data['mac_state'] = True
|
||||
url = reverse('horizon:admin:networks:editport',
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
@ -25,6 +26,8 @@ from openstack_dashboard import api
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
VNIC_TYPES = [('normal', _('Normal')), ('direct', _('Direct')),
|
||||
('macvtap', _('MacVTap'))]
|
||||
|
||||
|
||||
class UpdatePort(forms.SelfHandlingForm):
|
||||
@ -42,18 +45,42 @@ class UpdatePort(forms.SelfHandlingForm):
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(UpdatePort, self).__init__(request, *args, **kwargs)
|
||||
|
||||
if api.neutron.is_extension_supported(request, 'binding'):
|
||||
neutron_settings = getattr(settings,
|
||||
'OPENSTACK_NEUTRON_NETWORK', {})
|
||||
supported_vnic_types = neutron_settings.get(
|
||||
'supported_vnic_types', ['*'])
|
||||
if supported_vnic_types == ['*']:
|
||||
vnic_type_choices = VNIC_TYPES
|
||||
else:
|
||||
vnic_type_choices = [
|
||||
vnic_type for vnic_type in VNIC_TYPES
|
||||
if vnic_type[0] in supported_vnic_types
|
||||
]
|
||||
|
||||
self.fields['binding__vnic_type'] = forms.ChoiceField(
|
||||
choices=vnic_type_choices,
|
||||
label=_("Binding: VNIC Type"),
|
||||
help_text=_("The VNIC type that is bound to the neutron port"),
|
||||
required=False)
|
||||
|
||||
if api.neutron.is_extension_supported(request, 'mac-learning'):
|
||||
self.fields['mac_state'] = forms.BooleanField(
|
||||
label=_("Mac Learning State"), required=False)
|
||||
label=_("MAC Learning State"), initial=False, required=False)
|
||||
|
||||
def handle(self, request, data):
|
||||
data['admin_state'] = (data['admin_state'] == 'True')
|
||||
try:
|
||||
LOG.debug('params = %s' % data)
|
||||
extension_kwargs = {}
|
||||
if 'binding__vnic_type' in data:
|
||||
extension_kwargs['binding__vnic_type'] = \
|
||||
data['binding__vnic_type']
|
||||
if 'mac_state' in data:
|
||||
extension_kwargs['mac_learning_enabled'] = data['mac_state']
|
||||
port = api.neutron.port_update(request, data['port_id'],
|
||||
port = api.neutron.port_update(request,
|
||||
data['port_id'],
|
||||
name=data['name'],
|
||||
admin_state_up=data['admin_state'],
|
||||
**extension_kwargs)
|
||||
|
@ -100,8 +100,7 @@ class UpdateView(forms.ModalFormView):
|
||||
try:
|
||||
return api.neutron.port_get(self.request, port_id)
|
||||
except Exception:
|
||||
redirect = reverse("horizon:project:networks:detail",
|
||||
args=(self.kwargs['network_id'],))
|
||||
redirect = self.get_success_url()
|
||||
msg = _('Unable to retrieve port details')
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
|
||||
@ -120,9 +119,9 @@ class UpdateView(forms.ModalFormView):
|
||||
'network_id': port['network_id'],
|
||||
'tenant_id': port['tenant_id'],
|
||||
'name': port['name'],
|
||||
'admin_state': port['admin_state_up'],
|
||||
'device_id': port['device_id'],
|
||||
'device_owner': port['device_owner']}
|
||||
'admin_state': port['admin_state_up']}
|
||||
if port['binding__vnic_type']:
|
||||
initial['binding__vnic_type'] = port['binding__vnic_type']
|
||||
try:
|
||||
initial['mac_state'] = port['mac_learning_enabled']
|
||||
except Exception:
|
||||
|
@ -1,11 +1,7 @@
|
||||
{% load i18n sizeformat %}
|
||||
{% load url from future %}
|
||||
|
||||
<h3>{% trans "Port Overview" %}</h3>
|
||||
|
||||
<div class="info row detail">
|
||||
<h4>{% trans "Port" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<div class="detail">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ port.name|default:_("None") }}</dd>
|
||||
@ -16,18 +12,7 @@
|
||||
<dd><a href="{{ network_url }}">{{ port.network_id|default:_("None") }}</a></dd>
|
||||
<dt>{% trans "Project ID" %}</dt>
|
||||
<dd>{{ port.tenant_id|default:_("-") }}</dd>
|
||||
<dt>{% trans "Fixed IP" %}</dt>
|
||||
<dd>
|
||||
{% if port.fixed_ips.items|length > 1 %}
|
||||
{% for ip in port.fixed_ips %}
|
||||
<b>{% trans "IP address:" %}</b> {{ ip.ip_address }},
|
||||
<b>{% trans "Subnet ID" %}</b> {{ ip.subnet_id }}<br>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
{% trans "None" %}
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt>{% trans "Mac Address" %}</dt>
|
||||
<dt>{% trans "MAC Address" %}</dt>
|
||||
<dd>{{ port.mac_address|default:_("None") }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>{{ port.status_label|default:_("None") }}</dd>
|
||||
@ -37,12 +22,34 @@
|
||||
<dt>{% trans "MAC Learning State" %}</dt>
|
||||
<dd>{{ port.mac_state }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Attached Device" %}</dt>
|
||||
<h4>{% trans "Fixed IP" %}</h4>
|
||||
<hr class="header_rule">
|
||||
{% if port.fixed_ips.items|length > 1 %}
|
||||
{% for ip in port.fixed_ips %}
|
||||
<dt>{% trans "IP Address" %}</dt>
|
||||
<dd>{{ ip.ip_address }}</dd>
|
||||
{% url 'horizon:project:networks:subnets:detail' ip.subnet_id as subnet_url %}
|
||||
<dt>{% trans "Subnet ID" %}</dt>
|
||||
<dd><a href="{{ subnet_url }}">{{ ip.subnet_id }}</a></dd>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<dd>{% trans "None" %}</dd>
|
||||
{% endif %}
|
||||
<h4>{% trans "Attached Device" %}</h4>
|
||||
<hr class="header_rule">
|
||||
{% if port.device_id|length > 1 or port.device_owner %}
|
||||
<dd><b>{% trans "Device Owner" %}</b>: {{ port.device_owner|default:_("None") }}</dd>
|
||||
<dd><b>{% trans "Device ID" %}</b>: {{ port.device_id|default:_("-") }}</dd>
|
||||
<dt>{% trans "Device Owner" %}</dt>
|
||||
<dd>{{ port.device_owner|default:_("None") }}</dd>
|
||||
<dt>{% trans "Device ID" %}</dt>
|
||||
<dd>{{ port.device_id|default:_("None") }}</dd>
|
||||
{% else %}
|
||||
<dd>{% trans "No attached device" %}</dd>
|
||||
{% endif %}
|
||||
<h4>{% trans "Binding" %}</h4>
|
||||
<hr class="header_rule">
|
||||
{% if port.binding__vnic_type %}
|
||||
<dt>{% trans "VNIC Type" %}</dt>
|
||||
<dd>{{ port.binding__vnic_type }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
|
@ -1691,11 +1691,14 @@ class NetworkPortTests(test.TestCase):
|
||||
def test_port_update_get_with_mac_learning(self):
|
||||
self._test_port_update_get(mac_learning=True)
|
||||
|
||||
def _test_port_update_get(self, mac_learning=False):
|
||||
def _test_port_update_get(self, mac_learning=False, binding=False):
|
||||
port = self.ports.first()
|
||||
api.neutron.port_get(IsA(http.HttpRequest),
|
||||
port.id)\
|
||||
.AndReturn(port)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'binding')\
|
||||
.AndReturn(binding)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
@ -1719,14 +1722,19 @@ class NetworkPortTests(test.TestCase):
|
||||
def test_port_update_post_with_mac_learning(self):
|
||||
self._test_port_update_post(mac_learning=True)
|
||||
|
||||
def _test_port_update_post(self, mac_learning=False):
|
||||
def _test_port_update_post(self, mac_learning=False, binding=False):
|
||||
port = self.ports.first()
|
||||
api.neutron.port_get(IsA(http.HttpRequest), port.id)\
|
||||
.AndReturn(port)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'binding')\
|
||||
.AndReturn(binding)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
extension_kwargs = {}
|
||||
if binding:
|
||||
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type
|
||||
if mac_learning:
|
||||
extension_kwargs['mac_learning_enabled'] = True
|
||||
api.neutron.port_update(IsA(http.HttpRequest), port.id,
|
||||
@ -1740,6 +1748,8 @@ class NetworkPortTests(test.TestCase):
|
||||
'port_id': port.id,
|
||||
'name': port.name,
|
||||
'admin_state': port.admin_state_up}
|
||||
if binding:
|
||||
form_data['binding__vnic_type'] = port.binding__vnic_type
|
||||
if mac_learning:
|
||||
form_data['mac_state'] = True
|
||||
url = reverse('horizon:project:networks:editport',
|
||||
@ -1762,14 +1772,21 @@ class NetworkPortTests(test.TestCase):
|
||||
def test_port_update_post_exception_with_mac_learning(self):
|
||||
self._test_port_update_post_exception(mac_learning=True)
|
||||
|
||||
def _test_port_update_post_exception(self, mac_learning=False):
|
||||
def _test_port_update_post_exception(self, mac_learning=False,
|
||||
binding=False):
|
||||
|
||||
port = self.ports.first()
|
||||
api.neutron.port_get(IsA(http.HttpRequest), port.id)\
|
||||
.AndReturn(port)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'binding')\
|
||||
.AndReturn(binding)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'mac-learning')\
|
||||
.AndReturn(mac_learning)
|
||||
extension_kwargs = {}
|
||||
if binding:
|
||||
extension_kwargs['binding__vnic_type'] = port.binding__vnic_type
|
||||
if mac_learning:
|
||||
extension_kwargs['mac_learning_enabled'] = True
|
||||
api.neutron.port_update(IsA(http.HttpRequest), port.id,
|
||||
@ -1783,6 +1800,8 @@ class NetworkPortTests(test.TestCase):
|
||||
'port_id': port.id,
|
||||
'name': port.name,
|
||||
'admin_state': port.admin_state_up}
|
||||
if binding:
|
||||
form_data['binding__vnic_type'] = port.binding__vnic_type
|
||||
if mac_learning:
|
||||
form_data['mac_state'] = True
|
||||
url = reverse('horizon:project:networks:editport',
|
||||
|
@ -216,6 +216,12 @@ OPENSTACK_NEUTRON_NETWORK = {
|
||||
# in this list will be available to choose from when creating a network.
|
||||
# Network types include local, flat, vlan, gre, and vxlan.
|
||||
'supported_provider_types': ['*'],
|
||||
|
||||
# Set which VNIC types are supported for port binding. Only the VNIC
|
||||
# types in this list will be available to choose from when creating a
|
||||
# port.
|
||||
# VNIC types include 'normal', 'macvtap' and 'direct'.
|
||||
'supported_vnic_types': ['*']
|
||||
}
|
||||
|
||||
# The OPENSTACK_IMAGE_BACKEND settings can be used to customize features
|
||||
|
@ -161,7 +161,10 @@ def data(TEST):
|
||||
'name': '',
|
||||
'network_id': network_dict['id'],
|
||||
'status': 'ACTIVE',
|
||||
'tenant_id': network_dict['tenant_id']}
|
||||
'tenant_id': network_dict['tenant_id'],
|
||||
'binding:vnic_type': 'normal',
|
||||
'binding:host_id': 'host'}
|
||||
|
||||
TEST.api_ports.add(port_dict)
|
||||
TEST.ports.add(neutron.Port(port_dict))
|
||||
|
||||
@ -175,7 +178,9 @@ def data(TEST):
|
||||
'name': '',
|
||||
'network_id': network_dict['id'],
|
||||
'status': 'ACTIVE',
|
||||
'tenant_id': network_dict['tenant_id']}
|
||||
'tenant_id': network_dict['tenant_id'],
|
||||
'binding:vnic_type': 'normal',
|
||||
'binding:host_id': 'host'}
|
||||
TEST.api_ports.add(port_dict)
|
||||
TEST.ports.add(neutron.Port(port_dict))
|
||||
assoc_port = port_dict
|
||||
@ -190,7 +195,9 @@ def data(TEST):
|
||||
'name': '',
|
||||
'network_id': network_dict['id'],
|
||||
'status': 'ACTIVE',
|
||||
'tenant_id': network_dict['tenant_id']}
|
||||
'tenant_id': network_dict['tenant_id'],
|
||||
'binding:vnic_type': 'normal',
|
||||
'binding:host_id': 'host'}
|
||||
TEST.api_ports.add(port_dict)
|
||||
TEST.ports.add(neutron.Port(port_dict))
|
||||
|
||||
@ -238,7 +245,9 @@ def data(TEST):
|
||||
'name': '',
|
||||
'network_id': network_dict['id'],
|
||||
'status': 'ACTIVE',
|
||||
'tenant_id': network_dict['tenant_id']}
|
||||
'tenant_id': network_dict['tenant_id'],
|
||||
'binding:vnic_type': 'normal',
|
||||
'binding:host_id': 'host'}
|
||||
|
||||
TEST.api_ports.add(port_dict)
|
||||
TEST.ports.add(neutron.Port(port_dict))
|
||||
@ -350,7 +359,9 @@ def data(TEST):
|
||||
'name': '',
|
||||
'network_id': TEST.networks.get(name="ext_net")['id'],
|
||||
'status': 'ACTIVE',
|
||||
'tenant_id': '1'}
|
||||
'tenant_id': '1',
|
||||
'binding:vnic_type': 'normal',
|
||||
'binding:host_id': 'host'}
|
||||
TEST.api_ports.add(port_dict)
|
||||
TEST.ports.add(neutron.Port(port_dict))
|
||||
|
||||
@ -1142,6 +1153,8 @@ def data(TEST):
|
||||
'name': 'port5',
|
||||
'network_id': TEST.networks.get(name="net4")['id'],
|
||||
'status': 'ACTIVE',
|
||||
'tenant_id': TEST.networks.get(name="net4")['tenant_id']}
|
||||
'tenant_id': TEST.networks.get(name="net4")['tenant_id'],
|
||||
'binding:vnic_type': 'normal',
|
||||
'binding:host_id': 'host'}
|
||||
TEST.api_ports.add(port_dict)
|
||||
TEST.ports.add(neutron.Port(port_dict))
|
||||
|
Loading…
Reference in New Issue
Block a user