Add Cisco N1K plugin support in Horizon
This adds Horizon capability to configure and use network profiles and policy profiles on the Cisco Nexus 1000V to be used with networks and instances as is done via neutron CLI. To turn on the new dashboard the local_settings file needs to turn the profile_settings NEUTRON config variable to cisco. TODO items include creating a better palate of unit tests that will run not only when the cisco n1k subplugin is being used but in default setting as well. Need to remove the conditional statements in the unit tests and make it inclusive. Additionally need to get a better solution for how the dashboard/panel registration will work. Should it be a boolean or check for vendor specific names. Also some cleanup and refactoring of commonly used code everywhere. Implements: blueprint horizon-cisco-n1k Change-Id: If6e797ac28c8486cfa92e02742c409d193d9ec84
This commit is contained in:
parent
9ebb52b4e9
commit
38ba3df8b8
@ -93,6 +93,15 @@ class Port(NeutronAPIDictWrapper):
|
||||
super(Port, self).__init__(apiresource)
|
||||
|
||||
|
||||
class Profile(NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron profiles."""
|
||||
_attrs = ['profile_id', 'name', 'segment_type',
|
||||
'segment_range', 'multicast_ip_index', 'multicast_ip_range']
|
||||
|
||||
def __init__(self, apiresource):
|
||||
super(Profile, self).__init__(apiresource)
|
||||
|
||||
|
||||
class Router(NeutronAPIDictWrapper):
|
||||
"""Wrapper for neutron routers"""
|
||||
|
||||
@ -461,6 +470,9 @@ def network_create(request, **kwargs):
|
||||
:returns: Subnet object
|
||||
"""
|
||||
LOG.debug("network_create(): kwargs = %s" % kwargs)
|
||||
# In the case network profiles are being used, profile id is needed.
|
||||
if 'net_profile_id' in kwargs:
|
||||
kwargs['n1kv:profile_id'] = kwargs.pop('net_profile_id')
|
||||
body = {'network': kwargs}
|
||||
network = neutronclient(request).create_network(body=body).get('network')
|
||||
return Network(network)
|
||||
@ -551,6 +563,9 @@ def port_create(request, network_id, **kwargs):
|
||||
:returns: Port object
|
||||
"""
|
||||
LOG.debug("port_create(): netid=%s, kwargs=%s" % (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')
|
||||
body = {'port': {'network_id': network_id}}
|
||||
body['port'].update(kwargs)
|
||||
port = neutronclient(request).create_port(body=body).get('port')
|
||||
@ -569,6 +584,65 @@ def port_modify(request, port_id, **kwargs):
|
||||
return Port(port)
|
||||
|
||||
|
||||
def profile_list(request, type_p, **params):
|
||||
LOG.debug(_("profile_list(): "
|
||||
"profile_type=%(profile_type)s, params=%(params)s"),
|
||||
{'profile_type': type_p, 'params': params})
|
||||
if type_p == 'network':
|
||||
profiles = neutronclient(request).list_network_profiles(
|
||||
**params).get('network_profiles')
|
||||
elif type_p == 'policy':
|
||||
profiles = neutronclient(request).list_policy_profiles(
|
||||
**params).get('policy_profiles')
|
||||
return [Profile(n) for n in profiles]
|
||||
|
||||
|
||||
def profile_get(request, profile_id, **params):
|
||||
LOG.debug(_("profile_get(): "
|
||||
"profileid=%(profileid)s, params=%(params)s"),
|
||||
{'profileid': profile_id, 'params': params})
|
||||
profile = neutronclient(request).show_network_profile(
|
||||
profile_id, **params).get('network_profile')
|
||||
return Profile(profile)
|
||||
|
||||
|
||||
def profile_create(request, **kwargs):
|
||||
LOG.debug(_("profile_create(): kwargs=%s") % kwargs)
|
||||
body = {'network_profile': {}}
|
||||
body['network_profile'].update(kwargs)
|
||||
profile = neutronclient(request).create_network_profile(
|
||||
body=body).get('network_profile')
|
||||
return Profile(profile)
|
||||
|
||||
|
||||
def profile_delete(request, profile_id):
|
||||
LOG.debug(_("profile_delete(): profile_id=%s") % profile_id)
|
||||
neutronclient(request).delete_network_profile(profile_id)
|
||||
|
||||
|
||||
def profile_modify(request, profile_id, **kwargs):
|
||||
LOG.debug(_("profile_modify(): "
|
||||
"profileid=%(profileid)s, kwargs=%(kwargs)s"),
|
||||
{'profileid': profile_id, 'kwargs': kwargs})
|
||||
body = {'network_profile': kwargs}
|
||||
profile = neutronclient(request).update_network_profile(
|
||||
profile_id, body=body).get('network_profile')
|
||||
return Profile(profile)
|
||||
|
||||
|
||||
def profile_bindings_list(request, type_p, **params):
|
||||
LOG.debug(_("profile_bindings_list(): "
|
||||
"profile_type=%(profile_type)s params=%(params)s"),
|
||||
{'profile_type': type_p, 'params': params})
|
||||
if type_p == 'network':
|
||||
bindings = neutronclient(request).list_network_profile_bindings(
|
||||
**params).get('network_profile_bindings')
|
||||
elif type_p == 'policy':
|
||||
bindings = neutronclient(request).list_policy_profile_bindings(
|
||||
**params).get('policy_profile_bindings')
|
||||
return [Profile(n) for n in bindings]
|
||||
|
||||
|
||||
def router_create(request, **kwargs):
|
||||
LOG.debug("router_create():, kwargs=%s" % kwargs)
|
||||
body = {'router': {}}
|
||||
@ -659,3 +733,21 @@ def is_quotas_extension_supported(request):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
# Using this mechanism till a better plugin/sub-plugin detection
|
||||
# mechanism is available.
|
||||
# Using local_settings to detect if the "router" dashboard
|
||||
# should be turned on or not. When using specific plugins the
|
||||
# profile_support can be turned on if needed.
|
||||
# Since this is a temporary mechanism used to detect profile_support
|
||||
# @memorize is not being used. This is mainly used in the run_tests
|
||||
# environment to detect when to use profile_support neutron APIs.
|
||||
# TODO(absubram): Change this config variable check with
|
||||
# subplugin/plugin detection API when it becomes available.
|
||||
def is_port_profiles_supported():
|
||||
network_config = getattr(settings, 'OPENSTACK_NEUTRON_NETWORK', {})
|
||||
# Can be used to check for vendor specific plugin
|
||||
profile_support = network_config.get('profile_support', None)
|
||||
if str(profile_support).lower() == 'cisco':
|
||||
return True
|
||||
|
@ -17,6 +17,7 @@
|
||||
import logging
|
||||
|
||||
from django.core.urlresolvers import reverse # noqa
|
||||
from django.utils import datastructures # noqa
|
||||
from django.utils.translation import ugettext_lazy as _ # noqa
|
||||
|
||||
from horizon import exceptions
|
||||
@ -34,6 +35,8 @@ class CreateNetwork(forms.SelfHandlingForm):
|
||||
label=_("Name"),
|
||||
required=False)
|
||||
tenant_id = forms.ChoiceField(label=_("Project"))
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profile_id = forms.ChoiceField(label=_("Network Profile"))
|
||||
admin_state = forms.BooleanField(label=_("Admin State"),
|
||||
initial=True, required=False)
|
||||
shared = forms.BooleanField(label=_("Shared"),
|
||||
@ -54,6 +57,25 @@ class CreateNetwork(forms.SelfHandlingForm):
|
||||
tenant_choices.append((tenant.id, tenant.name))
|
||||
self.fields['tenant_id'].choices = tenant_choices
|
||||
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
self.fields['net_profile_id'].choices = (
|
||||
self.get_network_profile_choices(request))
|
||||
|
||||
def get_network_profile_choices(self, request):
|
||||
profile_choices = [('', _("Select a profile"))]
|
||||
for profile in self._get_profiles(request, 'network'):
|
||||
profile_choices.append((profile.id, profile.name))
|
||||
return profile_choices
|
||||
|
||||
def _get_profiles(self, request, type_p):
|
||||
profiles = []
|
||||
try:
|
||||
profiles = api.neutron.profile_list(request, type_p)
|
||||
except Exception:
|
||||
msg = _('Network Profiles could not be retrieved.')
|
||||
exceptions.handle(request, msg)
|
||||
return profiles
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
params = {'name': data['name'],
|
||||
@ -61,6 +83,8 @@ class CreateNetwork(forms.SelfHandlingForm):
|
||||
'admin_state_up': data['admin_state'],
|
||||
'shared': data['shared'],
|
||||
'router:external': data['external']}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
params['net_profile_id'] = data['net_profile_id']
|
||||
network = api.neutron.network_create(request, **params)
|
||||
msg = _('Network %s was successfully created.') % data['name']
|
||||
LOG.debug(msg)
|
||||
|
@ -149,11 +149,19 @@ class NetworkTests(test.BaseAdminViewTests):
|
||||
self.assertItemsEqual(subnets, [self.subnets.first()])
|
||||
self.assertEqual(len(ports), 0)
|
||||
|
||||
@test.create_stubs({api.keystone: ('tenant_list',)})
|
||||
@test.create_stubs({api.neutron: ('profile_list',),
|
||||
api.keystone: ('tenant_list',)})
|
||||
def test_network_create_get(self):
|
||||
tenants = self.tenants.list()
|
||||
api.keystone.tenant_list(IsA(http.HttpRequest))\
|
||||
.AndReturn([tenants, False])
|
||||
api.keystone.tenant_list(IsA(
|
||||
http.HttpRequest)).AndReturn([tenants, False])
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:admin:networks:create')
|
||||
@ -161,7 +169,8 @@ class NetworkTests(test.BaseAdminViewTests):
|
||||
|
||||
self.assertTemplateUsed(res, 'admin/networks/create.html')
|
||||
|
||||
@test.create_stubs({api.neutron: ('network_create',),
|
||||
@test.create_stubs({api.neutron: ('network_create',
|
||||
'profile_list',),
|
||||
api.keystone: ('tenant_list',)})
|
||||
def test_network_create_post(self):
|
||||
tenants = self.tenants.list()
|
||||
@ -174,6 +183,15 @@ class NetworkTests(test.BaseAdminViewTests):
|
||||
'admin_state_up': network.admin_state_up,
|
||||
'router:external': True,
|
||||
'shared': True}
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
net_profile_id = self.net_profiles.first().id
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
params['net_profile_id'] = net_profile_id
|
||||
api.neutron.network_create(IsA(http.HttpRequest), **params)\
|
||||
.AndReturn(network)
|
||||
self.mox.ReplayAll()
|
||||
@ -183,13 +201,16 @@ class NetworkTests(test.BaseAdminViewTests):
|
||||
'admin_state': network.admin_state_up,
|
||||
'external': True,
|
||||
'shared': True}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
form_data['net_profile_id'] = net_profile_id
|
||||
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.neutron: ('network_create',),
|
||||
@test.create_stubs({api.neutron: ('network_create',
|
||||
'profile_list',),
|
||||
api.keystone: ('tenant_list',)})
|
||||
def test_network_create_post_network_exception(self):
|
||||
tenants = self.tenants.list()
|
||||
@ -202,6 +223,15 @@ class NetworkTests(test.BaseAdminViewTests):
|
||||
'admin_state_up': network.admin_state_up,
|
||||
'router:external': True,
|
||||
'shared': False}
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
net_profile_id = self.net_profiles.first().id
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
params['net_profile_id'] = net_profile_id
|
||||
api.neutron.network_create(IsA(http.HttpRequest), **params)\
|
||||
.AndRaise(self.exceptions.neutron)
|
||||
self.mox.ReplayAll()
|
||||
@ -211,6 +241,8 @@ class NetworkTests(test.BaseAdminViewTests):
|
||||
'admin_state': network.admin_state_up,
|
||||
'external': True,
|
||||
'shared': False}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
form_data['net_profile_id'] = net_profile_id
|
||||
url = reverse('horizon:admin:networks:create')
|
||||
res = self.client.post(url, form_data)
|
||||
|
||||
|
@ -813,7 +813,8 @@ class InstanceTests(test.TestCase):
|
||||
api.network: ('security_group_list',),
|
||||
cinder: ('volume_snapshot_list',
|
||||
'volume_list',),
|
||||
api.neutron: ('network_list',),
|
||||
api.neutron: ('network_list',
|
||||
'profile_list',),
|
||||
api.glance: ('image_list_detailed',)})
|
||||
def test_launch_instance_get(self):
|
||||
image = self.images.first()
|
||||
@ -837,6 +838,13 @@ class InstanceTests(test.TestCase):
|
||||
api.neutron.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
policy_profiles = self.policy_profiles.list()
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'policy').AndReturn(policy_profiles)
|
||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest))\
|
||||
.AndReturn(self.limits['absolute'])
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
@ -878,7 +886,9 @@ class InstanceTests(test.TestCase):
|
||||
'<PostCreationStep: customizeaction>'])
|
||||
|
||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||
api.neutron: ('network_list',),
|
||||
api.neutron: ('network_list',
|
||||
'profile_list',
|
||||
'port_create',),
|
||||
api.nova: ('flavor_list',
|
||||
'keypair_list',
|
||||
'availability_zone_list',
|
||||
@ -921,6 +931,20 @@ class InstanceTests(test.TestCase):
|
||||
api.neutron.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
policy_profiles = self.policy_profiles.list()
|
||||
policy_profile_id = self.policy_profiles.first().id
|
||||
port = self.ports.first()
|
||||
api.neutron.profile_list(
|
||||
IsA(http.HttpRequest),
|
||||
'policy').AndReturn(policy_profiles)
|
||||
api.neutron.port_create(
|
||||
IsA(http.HttpRequest),
|
||||
network_id=self.networks.first().id,
|
||||
policy_profile_id=policy_profile_id).AndReturn(port)
|
||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn([])
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
|
||||
@ -970,7 +994,8 @@ class InstanceTests(test.TestCase):
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||
api.neutron: ('network_list',),
|
||||
api.neutron: ('network_list',
|
||||
'profile_list',),
|
||||
api.nova: ('flavor_list',
|
||||
'keypair_list',
|
||||
'availability_zone_list',
|
||||
@ -1016,6 +1041,21 @@ class InstanceTests(test.TestCase):
|
||||
api.neutron.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
policy_profiles = self.policy_profiles.list()
|
||||
policy_profile_id = self.policy_profiles.first().id
|
||||
port = self.ports.first()
|
||||
api.neutron.profile_list(
|
||||
IsA(http.HttpRequest),
|
||||
'policy').AndReturn(policy_profiles)
|
||||
api.neutron.port_create(
|
||||
IsA(http.HttpRequest),
|
||||
network_id=self.networks.first().id,
|
||||
policy_profile_id=policy_profile_id).AndReturn(port)
|
||||
nics = [{"port-id": port.id}]
|
||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
|
||||
@ -1067,7 +1107,9 @@ class InstanceTests(test.TestCase):
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||
api.neutron: ('network_list',),
|
||||
api.neutron: ('network_list',
|
||||
'profile_list',
|
||||
'port_create'),
|
||||
api.nova: ('server_create',
|
||||
'flavor_list',
|
||||
'keypair_list',
|
||||
@ -1115,6 +1157,21 @@ class InstanceTests(test.TestCase):
|
||||
api.neutron.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
policy_profiles = self.policy_profiles.list()
|
||||
policy_profile_id = self.policy_profiles.first().id
|
||||
port = self.ports.first()
|
||||
api.neutron.profile_list(
|
||||
IsA(http.HttpRequest),
|
||||
'policy').AndReturn(policy_profiles)
|
||||
api.neutron.port_create(
|
||||
IsA(http.HttpRequest),
|
||||
network_id=self.networks.first().id,
|
||||
policy_profile_id=policy_profile_id).AndReturn(port)
|
||||
nics = [{"port-id": port.id}]
|
||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
|
||||
@ -1168,7 +1225,8 @@ class InstanceTests(test.TestCase):
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||
api.neutron: ('network_list',),
|
||||
api.neutron: ('network_list',
|
||||
'profile_list',),
|
||||
api.nova: ('flavor_list',
|
||||
'keypair_list',
|
||||
'availability_zone_list',
|
||||
@ -1205,6 +1263,13 @@ class InstanceTests(test.TestCase):
|
||||
api.neutron.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
policy_profiles = self.policy_profiles.list()
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'policy').AndReturn(policy_profiles)
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.flavors.list())
|
||||
api.nova.keypair_list(IsA(http.HttpRequest)) \
|
||||
@ -1252,7 +1317,8 @@ class InstanceTests(test.TestCase):
|
||||
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
||||
|
||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||
api.neutron: ('network_list',),
|
||||
api.neutron: ('network_list',
|
||||
'profile_list',),
|
||||
cinder: ('volume_list',
|
||||
'volume_snapshot_list',),
|
||||
api.network: ('security_group_list',),
|
||||
@ -1280,6 +1346,13 @@ class InstanceTests(test.TestCase):
|
||||
api.neutron.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
policy_profiles = self.policy_profiles.list()
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'policy').AndReturn(policy_profiles)
|
||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.limits['absolute'])
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
@ -1309,7 +1382,9 @@ class InstanceTests(test.TestCase):
|
||||
self.assertTemplateUsed(res, views.WorkflowView.template_name)
|
||||
|
||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||
api.neutron: ('network_list',),
|
||||
api.neutron: ('network_list',
|
||||
'profile_list',
|
||||
'port_create',),
|
||||
api.nova: ('flavor_list',
|
||||
'keypair_list',
|
||||
'availability_zone_list',
|
||||
@ -1352,6 +1427,21 @@ class InstanceTests(test.TestCase):
|
||||
api.neutron.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
policy_profiles = self.policy_profiles.list()
|
||||
policy_profile_id = self.policy_profiles.first().id
|
||||
port = self.ports.first()
|
||||
api.neutron.profile_list(
|
||||
IsA(http.HttpRequest),
|
||||
'policy').AndReturn(policy_profiles)
|
||||
api.neutron.port_create(
|
||||
IsA(http.HttpRequest),
|
||||
network_id=self.networks.first().id,
|
||||
policy_profile_id=policy_profile_id).AndReturn(port)
|
||||
nics = [{"port-id": port.id}]
|
||||
cinder.volume_list(IgnoreArg()).AndReturn(self.volumes.list())
|
||||
api.glance.image_list_detailed(IsA(http.HttpRequest),
|
||||
filters={'is_public': True,
|
||||
@ -1403,7 +1493,8 @@ class InstanceTests(test.TestCase):
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.glance: ('image_list_detailed',),
|
||||
api.neutron: ('network_list',),
|
||||
api.neutron: ('network_list',
|
||||
'profile_list',),
|
||||
api.nova: ('flavor_list',
|
||||
'keypair_list',
|
||||
'tenant_absolute_limits',
|
||||
@ -1448,6 +1539,13 @@ class InstanceTests(test.TestCase):
|
||||
api.neutron.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
policy_profiles = self.policy_profiles.list()
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'policy').AndReturn(policy_profiles)
|
||||
cinder.volume_list(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.volumes.list())
|
||||
cinder.volume_snapshot_list(IsA(http.HttpRequest)).AndReturn([])
|
||||
@ -1551,7 +1649,8 @@ class InstanceTests(test.TestCase):
|
||||
api.network: ('security_group_list',),
|
||||
cinder: ('volume_snapshot_list',
|
||||
'volume_list',),
|
||||
api.neutron: ('network_list',),
|
||||
api.neutron: ('network_list',
|
||||
'profile_list'),
|
||||
api.glance: ('image_list_detailed',)})
|
||||
def test_select_default_keypair_if_only_one(self):
|
||||
keypair = self.keypairs.first()
|
||||
@ -1575,6 +1674,13 @@ class InstanceTests(test.TestCase):
|
||||
api.neutron.network_list(IsA(http.HttpRequest),
|
||||
shared=True) \
|
||||
.AndReturn(self.networks.list()[1:])
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
policy_profiles = self.policy_profiles.list()
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'policy').AndReturn(policy_profiles)
|
||||
api.nova.tenant_absolute_limits(IsA(http.HttpRequest)) \
|
||||
.AndReturn(self.limits['absolute'])
|
||||
api.nova.flavor_list(IsA(http.HttpRequest)) \
|
||||
|
@ -486,6 +486,11 @@ class SetNetworkAction(workflows.Action):
|
||||
" be specified.")},
|
||||
help_text=_("Launch instance with"
|
||||
" these networks"))
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
profile = forms.ChoiceField(label=_("Policy Profiles"),
|
||||
required=False,
|
||||
help_text=_("Launch instance with "
|
||||
"this policy profile"))
|
||||
|
||||
class Meta:
|
||||
name = _("Networking")
|
||||
@ -505,11 +510,26 @@ class SetNetworkAction(workflows.Action):
|
||||
_('Unable to retrieve networks.'))
|
||||
return network_list
|
||||
|
||||
def populate_profile_choices(self, request, context):
|
||||
try:
|
||||
profiles = api.neutron.profile_list(request, 'policy')
|
||||
profile_list = [(profile.id, profile.name) for profile in profiles]
|
||||
except Exception:
|
||||
profile_list = []
|
||||
exceptions.handle(request, _("Unable to retrieve profiles."))
|
||||
return profile_list
|
||||
|
||||
|
||||
class SetNetwork(workflows.Step):
|
||||
action_class = SetNetworkAction
|
||||
template_name = "project/instances/_update_networks.html"
|
||||
contributes = ("network_id",)
|
||||
# Disabling the template drag/drop only in the case port profiles
|
||||
# are used till the issue with the drag/drop affecting the
|
||||
# profile_id detection is fixed.
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
contributes = ("network_id", "profile_id",)
|
||||
else:
|
||||
template_name = "project/instances/_update_networks.html"
|
||||
contributes = ("network_id",)
|
||||
|
||||
def contribute(self, data, context):
|
||||
if data:
|
||||
@ -519,6 +539,9 @@ class SetNetwork(workflows.Step):
|
||||
networks = [n for n in networks if n != '']
|
||||
if networks:
|
||||
context['network_id'] = networks
|
||||
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
context['profile_id'] = data.get('profile', None)
|
||||
return context
|
||||
|
||||
|
||||
@ -583,6 +606,26 @@ class LaunchInstance(workflows.Workflow):
|
||||
|
||||
avail_zone = context.get('availability_zone', None)
|
||||
|
||||
# Create port with Network Name and Port Profile
|
||||
# for the use with the plugin supporting port profiles.
|
||||
# neutron port-create <Network name> --n1kv:profile <Port Profile ID>
|
||||
# for net_id in context['network_id']:
|
||||
## HACK for now use first network
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_id = context['network_id'][0]
|
||||
LOG.debug(_("Horizon->Create Port with %(netid)s %(profile_id)s"),
|
||||
{'netid': net_id, 'profile_id': context['profile_id']})
|
||||
try:
|
||||
port = api.neutron.port_create(request, net_id,
|
||||
policy_profile_id=
|
||||
context['profile_id'])
|
||||
except Exception:
|
||||
msg = (_('Port not created for profile-id (%s).') %
|
||||
context['profile_id'])
|
||||
exceptions.handle(request, msg)
|
||||
if port and port.id:
|
||||
nics = [{"port-id": port.id}]
|
||||
|
||||
try:
|
||||
api.nova.server_create(request,
|
||||
context['name'],
|
||||
|
@ -219,8 +219,15 @@ class NetworkTests(test.TestCase):
|
||||
self.assertItemsEqual(subnets, [self.subnets.first()])
|
||||
self.assertEqual(len(ports), 0)
|
||||
|
||||
@test.create_stubs({api.neutron: ('profile_list',)})
|
||||
def test_network_create_get(self):
|
||||
# no api methods are called.
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
url = reverse('horizon:project:networks:create')
|
||||
@ -234,18 +241,31 @@ class NetworkTests(test.TestCase):
|
||||
'<CreateSubnetDetail: createsubnetdetailaction>']
|
||||
self.assertQuerysetEqual(workflow.steps, expected_objs)
|
||||
|
||||
@test.create_stubs({api.neutron: ('network_create',)})
|
||||
@test.create_stubs({api.neutron: ('network_create',
|
||||
'profile_list',)})
|
||||
def test_network_create_post(self):
|
||||
network = self.networks.first()
|
||||
api.neutron.network_create(IsA(http.HttpRequest), name=network.name,
|
||||
admin_state_up=network.admin_state_up)\
|
||||
.AndReturn(network)
|
||||
params = {'name': network.name,
|
||||
'admin_state_up': network.admin_state_up}
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
net_profile_id = self.net_profiles.first().id
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
params['net_profile_id'] = net_profile_id
|
||||
api.neutron.network_create(IsA(http.HttpRequest),
|
||||
**params).AndReturn(network)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
form_data = {'net_name': network.name,
|
||||
'admin_state': network.admin_state_up,
|
||||
# subnet
|
||||
'with_subnet': False}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
form_data['net_profile_id'] = net_profile_id
|
||||
form_data.update(form_data_no_subnet())
|
||||
url = reverse('horizon:project:networks:create')
|
||||
res = self.client.post(url, form_data)
|
||||
@ -254,13 +274,24 @@ class NetworkTests(test.TestCase):
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.neutron: ('network_create',
|
||||
'subnet_create',)})
|
||||
'subnet_create',
|
||||
'profile_list',)})
|
||||
def test_network_create_post_with_subnet(self):
|
||||
network = self.networks.first()
|
||||
subnet = self.subnets.first()
|
||||
api.neutron.network_create(IsA(http.HttpRequest), name=network.name,
|
||||
admin_state_up=network.admin_state_up)\
|
||||
.AndReturn(network)
|
||||
params = {'name': network.name,
|
||||
'admin_state_up': network.admin_state_up}
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
net_profile_id = self.net_profiles.first().id
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
params['net_profile_id'] = net_profile_id
|
||||
api.neutron.network_create(IsA(http.HttpRequest),
|
||||
**params).AndReturn(network)
|
||||
api.neutron.subnet_create(IsA(http.HttpRequest),
|
||||
network_id=network.id,
|
||||
name=subnet.name,
|
||||
@ -274,6 +305,8 @@ class NetworkTests(test.TestCase):
|
||||
form_data = {'net_name': network.name,
|
||||
'admin_state': network.admin_state_up,
|
||||
'with_subnet': True}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
form_data['net_profile_id'] = net_profile_id
|
||||
form_data.update(form_data_subnet(subnet, allocation_pools=[]))
|
||||
url = reverse('horizon:project:networks:create')
|
||||
res = self.client.post(url, form_data)
|
||||
@ -281,18 +314,31 @@ class NetworkTests(test.TestCase):
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.neutron: ('network_create',)})
|
||||
@test.create_stubs({api.neutron: ('network_create',
|
||||
'profile_list',)})
|
||||
def test_network_create_post_network_exception(self):
|
||||
network = self.networks.first()
|
||||
api.neutron.network_create(IsA(http.HttpRequest), name=network.name,
|
||||
admin_state_up=network.admin_state_up)\
|
||||
.AndRaise(self.exceptions.neutron)
|
||||
params = {'name': network.name,
|
||||
'admin_state_up': network.admin_state_up}
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
net_profile_id = self.net_profiles.first().id
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
params['net_profile_id'] = net_profile_id
|
||||
api.neutron.network_create(IsA(http.HttpRequest),
|
||||
**params).AndRaise(self.exceptions.neutron)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
form_data = {'net_name': network.name,
|
||||
'admin_state': network.admin_state_up,
|
||||
# subnet
|
||||
'with_subnet': False}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
form_data['net_profile_id'] = net_profile_id
|
||||
form_data.update(form_data_no_subnet())
|
||||
url = reverse('horizon:project:networks:create')
|
||||
res = self.client.post(url, form_data)
|
||||
@ -300,18 +346,31 @@ class NetworkTests(test.TestCase):
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.neutron: ('network_create',)})
|
||||
@test.create_stubs({api.neutron: ('network_create',
|
||||
'profile_list')})
|
||||
def test_network_create_post_with_subnet_network_exception(self):
|
||||
network = self.networks.first()
|
||||
subnet = self.subnets.first()
|
||||
api.neutron.network_create(IsA(http.HttpRequest), name=network.name,
|
||||
admin_state_up=network.admin_state_up)\
|
||||
.AndRaise(self.exceptions.neutron)
|
||||
params = {'name': network.name,
|
||||
'admin_state_up': network.admin_state_up}
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
net_profile_id = self.net_profiles.first().id
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
params['net_profile_id'] = net_profile_id
|
||||
api.neutron.network_create(IsA(http.HttpRequest),
|
||||
**params).AndRaise(self.exceptions.neutron)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
form_data = {'net_name': network.name,
|
||||
'admin_state': network.admin_state_up,
|
||||
'with_subnet': True}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
form_data['net_profile_id'] = net_profile_id
|
||||
form_data.update(form_data_subnet(subnet, allocation_pools=[]))
|
||||
url = reverse('horizon:project:networks:create')
|
||||
res = self.client.post(url, form_data)
|
||||
@ -321,13 +380,24 @@ class NetworkTests(test.TestCase):
|
||||
|
||||
@test.create_stubs({api.neutron: ('network_create',
|
||||
'network_delete',
|
||||
'subnet_create',)})
|
||||
'subnet_create',
|
||||
'profile_list')})
|
||||
def test_network_create_post_with_subnet_subnet_exception(self):
|
||||
network = self.networks.first()
|
||||
subnet = self.subnets.first()
|
||||
api.neutron.network_create(IsA(http.HttpRequest), name=network.name,
|
||||
admin_state_up=network.admin_state_up)\
|
||||
.AndReturn(network)
|
||||
params = {'name': network.name,
|
||||
'admin_state_up': network.admin_state_up}
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
net_profile_id = self.net_profiles.first().id
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
params['net_profile_id'] = net_profile_id
|
||||
api.neutron.network_create(IsA(http.HttpRequest),
|
||||
**params).AndReturn(network)
|
||||
api.neutron.subnet_create(IsA(http.HttpRequest),
|
||||
network_id=network.id,
|
||||
name=subnet.name,
|
||||
@ -343,6 +413,8 @@ class NetworkTests(test.TestCase):
|
||||
form_data = {'net_name': network.name,
|
||||
'admin_state': network.admin_state_up,
|
||||
'with_subnet': True}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
form_data['net_profile_id'] = net_profile_id
|
||||
form_data.update(form_data_subnet(subnet, allocation_pools=[]))
|
||||
url = reverse('horizon:project:networks:create')
|
||||
res = self.client.post(url, form_data)
|
||||
@ -350,14 +422,25 @@ class NetworkTests(test.TestCase):
|
||||
self.assertNoFormErrors(res)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
@test.create_stubs({api.neutron: ('profile_list',)})
|
||||
def test_network_create_post_with_subnet_nocidr(self):
|
||||
network = self.networks.first()
|
||||
subnet = self.subnets.first()
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
net_profile_id = self.net_profiles.first().id
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
form_data = {'net_name': network.name,
|
||||
'admin_state': network.admin_state_up,
|
||||
'with_subnet': True}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
form_data['net_profile_id'] = net_profile_id
|
||||
form_data.update(form_data_subnet(subnet, cidr='',
|
||||
allocation_pools=[]))
|
||||
url = reverse('horizon:project:networks:create')
|
||||
@ -366,13 +449,25 @@ class NetworkTests(test.TestCase):
|
||||
self.assertContains(res, escape('Specify "Network Address" or '
|
||||
'clear "Create Subnet" checkbox.'))
|
||||
|
||||
@test.create_stubs({api.neutron: ('profile_list',)})
|
||||
def test_network_create_post_with_subnet_cidr_without_mask(self):
|
||||
network = self.networks.first()
|
||||
subnet = self.subnets.first()
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
net_profile_id = self.net_profiles.first().id
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
form_data = {'net_name': network.name,
|
||||
'admin_state': network.admin_state_up,
|
||||
'with_subnet': True}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
form_data['net_profile_id'] = net_profile_id
|
||||
form_data.update(form_data_subnet(subnet, cidr='10.0.0.0',
|
||||
allocation_pools=[]))
|
||||
url = reverse('horizon:project:networks:create')
|
||||
@ -381,9 +476,18 @@ class NetworkTests(test.TestCase):
|
||||
expected_msg = "The subnet in the Network Address is too small (/32)."
|
||||
self.assertContains(res, expected_msg)
|
||||
|
||||
@test.create_stubs({api.neutron: ('profile_list',)})
|
||||
def test_network_create_post_with_subnet_cidr_inconsistent(self):
|
||||
network = self.networks.first()
|
||||
subnet = self.subnets.first()
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
net_profile_id = self.net_profiles.first().id
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
# dummy IPv6 address
|
||||
@ -391,6 +495,8 @@ class NetworkTests(test.TestCase):
|
||||
form_data = {'net_name': network.name,
|
||||
'admin_state': network.admin_state_up,
|
||||
'with_subnet': True}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
form_data['net_profile_id'] = net_profile_id
|
||||
form_data.update(form_data_subnet(subnet, cidr=cidr,
|
||||
allocation_pools=[]))
|
||||
url = reverse('horizon:project:networks:create')
|
||||
@ -399,9 +505,18 @@ class NetworkTests(test.TestCase):
|
||||
expected_msg = 'Network Address and IP version are inconsistent.'
|
||||
self.assertContains(res, expected_msg)
|
||||
|
||||
@test.create_stubs({api.neutron: ('profile_list',)})
|
||||
def test_network_create_post_with_subnet_gw_inconsistent(self):
|
||||
network = self.networks.first()
|
||||
subnet = self.subnets.first()
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profiles = self.net_profiles.list()
|
||||
net_profile_id = self.net_profiles.first().id
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
# dummy IPv6 address
|
||||
@ -409,6 +524,8 @@ class NetworkTests(test.TestCase):
|
||||
form_data = {'net_name': network.name,
|
||||
'admin_state': network.admin_state_up,
|
||||
'with_subnet': True}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
form_data['net_profile_id'] = net_profile_id
|
||||
form_data.update(form_data_subnet(subnet, gateway_ip=gateway_ip,
|
||||
allocation_pools=[]))
|
||||
url = reverse('horizon:project:networks:create')
|
||||
|
@ -37,9 +37,35 @@ class CreateNetworkInfoAction(workflows.Action):
|
||||
net_name = forms.CharField(max_length=255,
|
||||
label=_("Network Name"),
|
||||
required=False)
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
net_profile_id = forms.ChoiceField(label=_("Network Profile"))
|
||||
admin_state = forms.BooleanField(label=_("Admin State"),
|
||||
initial=True, required=False)
|
||||
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(CreateNetworkInfoAction, self).__init__(request,
|
||||
*args, **kwargs)
|
||||
self.fields['net_profile_id'].choices = (
|
||||
self.get_network_profile_choices(request))
|
||||
|
||||
def get_network_profile_choices(self, request):
|
||||
profile_choices = [('', _("Select a profile"))]
|
||||
for profile in self._get_profiles(request, 'network'):
|
||||
profile_choices.append((profile.id, profile.name))
|
||||
return profile_choices
|
||||
|
||||
def _get_profiles(self, request, type_p):
|
||||
try:
|
||||
profiles = api.neutron.profile_list(request, type_p)
|
||||
except Exception:
|
||||
profiles = []
|
||||
msg = _('Network Profiles could not be retrieved.')
|
||||
exceptions.handle(request, msg)
|
||||
return profiles
|
||||
# TODO(absubram): Add ability to view network profile information
|
||||
# in the network detail if a profile is used.
|
||||
|
||||
class Meta:
|
||||
name = _("Network")
|
||||
help_text = _("From here you can create a new network.\n"
|
||||
@ -49,7 +75,10 @@ class CreateNetworkInfoAction(workflows.Action):
|
||||
|
||||
class CreateNetworkInfo(workflows.Step):
|
||||
action_class = CreateNetworkInfoAction
|
||||
contributes = ("net_name", "admin_state")
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
contributes = ("net_name", "admin_state", "net_profile_id")
|
||||
else:
|
||||
contributes = ("net_name", "admin_state")
|
||||
|
||||
|
||||
class CreateSubnetInfoAction(workflows.Action):
|
||||
@ -257,6 +286,8 @@ class CreateNetwork(workflows.Workflow):
|
||||
try:
|
||||
params = {'name': data['net_name'],
|
||||
'admin_state_up': data['admin_state']}
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
params['net_profile_id'] = data['net_profile_id']
|
||||
network = api.neutron.network_create(request, **params)
|
||||
network.set_id_as_name_if_empty()
|
||||
self.context['net_id'] = network.id
|
||||
|
0
openstack_dashboard/dashboards/router/__init__.py
Normal file
0
openstack_dashboard/dashboards/router/__init__.py
Normal file
33
openstack_dashboard/dashboards/router/dashboard.py
Normal file
33
openstack_dashboard/dashboards/router/dashboard.py
Normal file
@ -0,0 +1,33 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# 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.
|
||||
#
|
||||
# @author: Abishek Subramanian, Cisco Systems, Inc.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _ # noqa
|
||||
|
||||
from openstack_dashboard.api import neutron
|
||||
|
||||
import horizon
|
||||
|
||||
|
||||
class Router(horizon.Dashboard):
|
||||
name = _("Router")
|
||||
slug = "router"
|
||||
panels = ('nexus1000v',)
|
||||
default_panel = 'nexus1000v'
|
||||
permissions = ('openstack.roles.admin',)
|
||||
|
||||
|
||||
if neutron.is_port_profiles_supported():
|
||||
horizon.register(Router)
|
3
openstack_dashboard/dashboards/router/models.py
Normal file
3
openstack_dashboard/dashboards/router/models.py
Normal file
@ -0,0 +1,3 @@
|
||||
"""
|
||||
Stub file to work around django bug: https://code.djangoproject.com/ticket/7198
|
||||
"""
|
159
openstack_dashboard/dashboards/router/nexus1000v/forms.py
Normal file
159
openstack_dashboard/dashboards/router/nexus1000v/forms.py
Normal file
@ -0,0 +1,159 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# 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.
|
||||
#
|
||||
# @author: Abishek Subramanian, Cisco Systems, Inc.
|
||||
# @author: Sergey Sudakovich, Cisco Systems, Inc.
|
||||
|
||||
import logging
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from django.core.urlresolvers import reverse # noqa
|
||||
from django.utils.translation import ugettext_lazy as _ # noqa
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_tenant_choices(request):
|
||||
tenant_choices = [('', _("Select a tenant"))]
|
||||
tenants = []
|
||||
try:
|
||||
tenants, has_more = api.keystone.tenant_list(request)
|
||||
except Exception:
|
||||
msg = _('Projects could not be retrieved.')
|
||||
exceptions.handle(request, msg)
|
||||
for tenant in tenants:
|
||||
if tenant.enabled:
|
||||
tenant_choices.append((tenant.id, tenant.name))
|
||||
return tenant_choices
|
||||
|
||||
|
||||
class CreateNetworkProfile(forms.SelfHandlingForm):
|
||||
|
||||
""" Create Network Profile form."""
|
||||
|
||||
name = forms.CharField(max_length=255,
|
||||
label=_("Name"),
|
||||
required=True)
|
||||
segment_type = forms.ChoiceField(label=_('Segment Type'),
|
||||
choices=[('vlan', _('VLAN')),
|
||||
('vxlan', _('VXLAN'))],
|
||||
widget=forms.Select
|
||||
(attrs={'class': 'switchable',
|
||||
'data-slug': 'segtype'}))
|
||||
segment_range = forms.CharField(max_length=255,
|
||||
label=_("Segment Range"),
|
||||
required=True,
|
||||
help_text=_("1-4093 for VLAN"))
|
||||
# TODO(absubram): Update help text for VXLAN segment range value.
|
||||
multicast_ip_range = forms.CharField(max_length=30,
|
||||
label=_("Multicast IP Range"),
|
||||
required=False,
|
||||
widget=forms.TextInput
|
||||
(attrs={'class': 'switched',
|
||||
'data-switch-on':
|
||||
'segtype',
|
||||
'data-segtype-vxlan':
|
||||
_("Multicast IP Range")}))
|
||||
physical_network = forms.CharField(max_length=255,
|
||||
label=_("Physical Network"),
|
||||
required=False,
|
||||
widget=forms.TextInput
|
||||
(attrs={'class': 'switched',
|
||||
'data-switch-on': 'segtype',
|
||||
'data-segtype-vlan':
|
||||
_("Physical Network")}))
|
||||
project_id = forms.ChoiceField(label=_("Project"),
|
||||
required=False)
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(CreateNetworkProfile, self).__init__(request, *args, **kwargs)
|
||||
self.fields['project_id'].choices = get_tenant_choices(request)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
LOG.debug(_('request = %(req)s, params = %(params)s'),
|
||||
{'req': request, 'params': data})
|
||||
profile = api.neutron.profile_create(request,
|
||||
name=data['name'],
|
||||
segment_type=
|
||||
data['segment_type'],
|
||||
segment_range=
|
||||
data['segment_range'],
|
||||
physical_network=
|
||||
data['physical_network'],
|
||||
multicast_ip_range=
|
||||
data['multicast_ip_range'],
|
||||
tenant_id=data['project_id'])
|
||||
msg = _('Network Profile %s '
|
||||
'was successfully created.') % data['name']
|
||||
LOG.debug(msg)
|
||||
messages.success(request, msg)
|
||||
return profile
|
||||
except Exception:
|
||||
redirect = reverse('horizon:router:nexus1000v:index')
|
||||
msg = _('Failed to create network profile %s') % data['name']
|
||||
LOG.error(msg)
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
|
||||
|
||||
class UpdateNetworkProfile(forms.SelfHandlingForm):
|
||||
|
||||
""" Update Network Profile form."""
|
||||
|
||||
profile_id = forms.CharField(label=_("ID"),
|
||||
widget=forms.HiddenInput())
|
||||
name = forms.CharField(max_length=255,
|
||||
label=_("Name"), required=True)
|
||||
segment_type = forms.ChoiceField(label=_('Segment Type'),
|
||||
choices=[('vlan', 'VLAN'),
|
||||
('vxlan', 'VXLAN')],
|
||||
widget=forms.Select
|
||||
(attrs={'class': 'switchable'}))
|
||||
segment_range = forms.CharField(max_length=255,
|
||||
label=_("Segment Range"),
|
||||
required=True)
|
||||
physical_network = forms.CharField(max_length=255,
|
||||
label=_("Physical Network"),
|
||||
required=False)
|
||||
project_id = forms.CharField(label=_("Project"), required=False)
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
LOG.debug(_('request = %(req)s, params = %(params)s'),
|
||||
{'req': request, 'params': data})
|
||||
profile = api.neutron.profile_modify(request,
|
||||
data['profile_id'],
|
||||
name=data['name'],
|
||||
segment_type=
|
||||
data['segment_type'],
|
||||
segment_range=
|
||||
data['segment_range'],
|
||||
physical_network=
|
||||
data['physical_network'])
|
||||
msg = _('Network Profile %s '
|
||||
'was successfully updated.') % data['profile_id']
|
||||
LOG.debug(msg)
|
||||
messages.success(request, msg)
|
||||
return profile
|
||||
except Exception:
|
||||
LOG.error(_('Failed to update network profile (%s).') %
|
||||
data['profile_id'])
|
||||
redirect = reverse('horizon:router:nexus1000v:index')
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
31
openstack_dashboard/dashboards/router/nexus1000v/panel.py
Normal file
31
openstack_dashboard/dashboards/router/nexus1000v/panel.py
Normal file
@ -0,0 +1,31 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# 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.
|
||||
#
|
||||
# @author: Abishek Subramanian, Cisco Systems, Inc.
|
||||
# @author: Sergey Sudakovich, Cisco Systems, Inc.
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _ # noqa
|
||||
|
||||
import horizon
|
||||
from openstack_dashboard.api import neutron as neutron
|
||||
from openstack_dashboard.dashboards.router import dashboard
|
||||
|
||||
|
||||
class Nexus1000v(horizon.Panel):
|
||||
name = _("Cisco Nexus 1000v")
|
||||
slug = 'nexus1000v'
|
||||
permissions = ('openstack.services.network',)
|
||||
|
||||
if neutron.is_port_profiles_supported():
|
||||
dashboard.Router.register(Nexus1000v)
|
93
openstack_dashboard/dashboards/router/nexus1000v/tables.py
Normal file
93
openstack_dashboard/dashboards/router/nexus1000v/tables.py
Normal file
@ -0,0 +1,93 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# 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.
|
||||
#
|
||||
# @author: Abishek Subramanian, Cisco Systems, Inc.
|
||||
# @author: Sergey Sudakovich, Cisco Systems, Inc.
|
||||
|
||||
import logging
|
||||
|
||||
from django.core.urlresolvers import reverse # noqa
|
||||
from django.utils.translation import ugettext_lazy as _ # noqa
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tables
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateNetworkProfile(tables.LinkAction):
|
||||
name = "create"
|
||||
verbose_name = _("Create Network Profile")
|
||||
url = "horizon:router:nexus1000v:create_network_profile"
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
|
||||
|
||||
class DeleteNetworkProfile(tables.DeleteAction):
|
||||
data_type_singular = _("Network Profile")
|
||||
data_type_plural = _("Netork Profiles")
|
||||
|
||||
def delete(self, request, obj_id):
|
||||
try:
|
||||
api.neutron.profile_delete(request, obj_id)
|
||||
except Exception:
|
||||
msg = _('Failed to delete network profile (%s).') % obj_id
|
||||
LOG.info(msg)
|
||||
redirect = reverse('horizon:router:nexus1000v:index')
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
|
||||
|
||||
class EditNetworkProfile(tables.LinkAction):
|
||||
name = "update"
|
||||
verbose_name = _("Edit Network Profile")
|
||||
url = "horizon:router:nexus1000v:update_network_profile"
|
||||
classes = ("ajax-modal", "btn-edit")
|
||||
|
||||
|
||||
class NetworkProfile(tables.DataTable):
|
||||
id = tables.Column("profile_id", verbose_name=_("Profile ID"), hidden=True)
|
||||
name = tables.Column("name", verbose_name=_("Network Profile"), )
|
||||
project = tables.Column("project_name", verbose_name=_("Project"))
|
||||
segment_type = tables.Column("segment_type",
|
||||
verbose_name=_("Segment Type"))
|
||||
segment_range = tables.Column("segment_range",
|
||||
verbose_name=_("Segment Range"))
|
||||
multicast_ip_range = tables.Column("multicast_ip_range",
|
||||
verbose_name=_("Multicast IP Range"))
|
||||
physical_network = tables.Column("physical_network",
|
||||
verbose_name=_("Physical Network Name"))
|
||||
|
||||
class Meta:
|
||||
name = "network_profile"
|
||||
verbose_name = _("Network Profile")
|
||||
table_actions = (CreateNetworkProfile, DeleteNetworkProfile,)
|
||||
row_actions = (EditNetworkProfile, DeleteNetworkProfile,)
|
||||
|
||||
|
||||
class EditPolicyProfile(tables.LinkAction):
|
||||
name = "edit"
|
||||
verbose_name = _("Edit Policy Profile")
|
||||
url = "horizon:project:images_and_snapshots:images:update"
|
||||
classes = ("ajax-modal", "btn-edit")
|
||||
|
||||
|
||||
class PolicyProfile(tables.DataTable):
|
||||
id = tables.Column("profile_id", verbose_name=_("Profile ID"), hidden=True)
|
||||
name = tables.Column("name", verbose_name=_("Policy Profile"), )
|
||||
project_id = tables.Column("project_name", verbose_name=_("Project"))
|
||||
|
||||
class Meta:
|
||||
name = "policy_profile"
|
||||
verbose_name = _("Policy Profile")
|
41
openstack_dashboard/dashboards/router/nexus1000v/tabs.py
Normal file
41
openstack_dashboard/dashboards/router/nexus1000v/tabs.py
Normal file
@ -0,0 +1,41 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# 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.
|
||||
#
|
||||
# @author: Abishek Subramanian, Cisco Systems, Inc.
|
||||
# @author: Sergey Sudakovich, Cisco Systems, Inc.
|
||||
|
||||
from django.utils.translation import ugettext as _ # noqa
|
||||
|
||||
from horizon import tabs
|
||||
|
||||
|
||||
class NetworkProfileTab(tabs.Tab):
|
||||
name = _("Network Profile")
|
||||
slug = "network_profile"
|
||||
template_name = 'router/nexus1000v/network_profile/index.html'
|
||||
|
||||
def get_context_data(self, request):
|
||||
return None
|
||||
|
||||
|
||||
class PolicyProfileTab(tabs.Tab):
|
||||
name = _("Policy Profile")
|
||||
slug = "policy_profile"
|
||||
template_name = 'router/nexus1000v/policy_profile/index.html'
|
||||
preload = False
|
||||
|
||||
|
||||
class IndexTabs(tabs.TabGroup):
|
||||
slug = "indextabs"
|
||||
tabs = (NetworkProfileTab, PolicyProfileTab)
|
@ -0,0 +1,26 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}create_network_profile_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:router:nexus1000v:create_network_profile' %}{% endblock %}
|
||||
|
||||
{% block modal_id %}create_network_profile_modal{% endblock %}
|
||||
{% block modal-header %}{% trans "Create Network Profile" %}{% 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 profile."%}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Network Profile" %}" />
|
||||
<a href="{% url 'horizon:router:nexus1000v:index' %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -0,0 +1,25 @@
|
||||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load url from future %}
|
||||
|
||||
{% block form_id %}update_networkprofile_form{% endblock %}
|
||||
{% block form_action %}{% url 'horizon:router:nexus1000v:update_network_profile profile_id' %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Edit Network Profile" %}{% 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 profile here." %}</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Save Changes" %}" />
|
||||
<a href="{% url horizon:router:nexus1000v:index %}" class="btn secondary cancel close">{% trans "Cancel" %}</a>
|
||||
{% endblock %}
|
@ -0,0 +1,11 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create Network Profile" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Create Network Profile") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include "router/nexus1000v/_create_network_profile.html" %}
|
||||
{% endblock %}
|
@ -0,0 +1,23 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Cisco Nexus 1000V Networking" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Cisco Nexus 1000V") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div id="network_profile">
|
||||
{{ network_profile_table.render }}
|
||||
</div>
|
||||
<div id="policy_profile">
|
||||
{{ policy_profile_table.render }}
|
||||
</div>
|
||||
{% comment %}
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
{% endblock %}
|
@ -0,0 +1,13 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Cisco Nexus 1000V Networking" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Cisco Nexus 1000V") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div id="network_profile">
|
||||
{{ table.render }}
|
||||
</div>
|
||||
{% endblock %}
|
@ -0,0 +1,13 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Cisco Nexus 1000V Networking" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Cisco Nexus 1000V") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div id="policy_profile">
|
||||
{{ table.render }}
|
||||
</div>
|
||||
{% endblock %}
|
@ -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 Profile") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'router/nexus1000v/_update_network_profile.html' %}
|
||||
{% endblock %}
|
57
openstack_dashboard/dashboards/router/nexus1000v/tests.py
Normal file
57
openstack_dashboard/dashboards/router/nexus1000v/tests.py
Normal file
@ -0,0 +1,57 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# 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.
|
||||
#
|
||||
# @author: Abishek Subramanian, Cisco Systems, Inc.
|
||||
|
||||
from django.core.urlresolvers import reverse # noqa
|
||||
from django import http
|
||||
|
||||
from mox import IsA # noqa
|
||||
|
||||
from openstack_dashboard import api
|
||||
from openstack_dashboard.test import helpers as test
|
||||
|
||||
|
||||
# TODO(absubram): Remove if clause and create separate
|
||||
# test stubs for when profile_support is being used and when not.
|
||||
# Additionally ensure those are always run even in default setting
|
||||
if api.neutron.is_port_profiles_supported():
|
||||
class Nexus1000vTest(test.BaseAdminViewTests):
|
||||
@test.create_stubs({api.neutron: ('profile_list',
|
||||
'profile_bindings_list'),
|
||||
api.keystone: ('tenant_list',)})
|
||||
def test_index(self):
|
||||
tenants = self.tenants.list()
|
||||
net_profiles = self.net_profiles.list()
|
||||
policy_profiles = self.policy_profiles.list()
|
||||
net_profile_binding = self.network_profile_binding.list()
|
||||
policy_profile_binding = self.policy_profile_binding.list()
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profiles)
|
||||
api.neutron.profile_list(IsA(http.HttpRequest),
|
||||
'policy').AndReturn(policy_profiles)
|
||||
api.neutron.profile_bindings_list(
|
||||
IsA(http.HttpRequest),
|
||||
'network').AndReturn(net_profile_binding)
|
||||
api.neutron.profile_bindings_list(
|
||||
IsA(http.HttpRequest),
|
||||
'policy').AndReturn(policy_profile_binding)
|
||||
api.keystone.tenant_list(
|
||||
IsA(http.HttpRequest)).AndReturn([tenants, False])
|
||||
api.keystone.tenant_list(
|
||||
IsA(http.HttpRequest)).AndReturn([tenants, False])
|
||||
self.mox.ReplayAll()
|
||||
|
||||
res = self.client.get(reverse('horizon:router:nexus1000v:index'))
|
||||
self.assertTemplateUsed(res, 'router/nexus1000v/index.html')
|
32
openstack_dashboard/dashboards/router/nexus1000v/urls.py
Normal file
32
openstack_dashboard/dashboards/router/nexus1000v/urls.py
Normal file
@ -0,0 +1,32 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# 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.
|
||||
#
|
||||
# @author: Abishek Subramanian, Cisco Systems, Inc.
|
||||
# @author: Sergey Sudakovich, Cisco Systems, Inc.
|
||||
|
||||
from django.conf.urls.defaults import patterns # noqa
|
||||
from django.conf.urls.defaults import url # noqa
|
||||
|
||||
from openstack_dashboard.dashboards.router.nexus1000v import views
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
#Network Profile
|
||||
url(r'^network_profile/create$', views.CreateNetworkProfileView.as_view(),
|
||||
name='create_network_profile'),
|
||||
url(r'^network_profile/(?P<profile_id>[^/]+)/update$',
|
||||
views.UpdateNetworkProfileView.as_view(),
|
||||
name='update_network_profile'),
|
||||
|
||||
)
|
141
openstack_dashboard/dashboards/router/nexus1000v/views.py
Normal file
141
openstack_dashboard/dashboards/router/nexus1000v/views.py
Normal file
@ -0,0 +1,141 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# 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.
|
||||
#
|
||||
# @author: Abishek Subramanian, Cisco Systems, Inc.
|
||||
# @author: Sergey Sudakovich, Cisco Systems, Inc.
|
||||
|
||||
import logging
|
||||
|
||||
from django.core import urlresolvers
|
||||
from django.utils import datastructures
|
||||
from django.utils.translation import ugettext_lazy as _ # noqa
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tables
|
||||
from horizon import tabs
|
||||
|
||||
from openstack_dashboard import api
|
||||
|
||||
from openstack_dashboard.dashboards.router.nexus1000v \
|
||||
import forms as profileforms
|
||||
from openstack_dashboard.dashboards.router.nexus1000v \
|
||||
import tables as profiletables
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _get_tenant_list(request):
|
||||
tenants = []
|
||||
try:
|
||||
tenants, has_more = api.keystone.tenant_list(request)
|
||||
except Exception:
|
||||
msg = _('Unable to retrieve project information.')
|
||||
exceptions.handle(request, msg)
|
||||
|
||||
return datastructures.SortedDict([(t.id, t) for t in tenants])
|
||||
|
||||
|
||||
def _get_profiles(request, type_p):
|
||||
try:
|
||||
profiles = api.neutron.profile_list(request, type_p)
|
||||
except Exception:
|
||||
profiles = []
|
||||
msg = _('Network Profiles could not be retrieved.')
|
||||
exceptions.handle(request, msg)
|
||||
if profiles:
|
||||
tenant_dict = _get_tenant_list(request)
|
||||
bindings = api.neutron.profile_bindings_list(request, type_p)
|
||||
for p in profiles:
|
||||
# Set tenant name
|
||||
if bindings:
|
||||
for b in bindings:
|
||||
if (p.id == b.profile_id):
|
||||
tenant = tenant_dict.get(b.tenant_id, None)
|
||||
p.tenant_name = getattr(tenant, 'name', None)
|
||||
return profiles
|
||||
|
||||
|
||||
class NetworkProfileIndexView(tables.DataTableView):
|
||||
table_class = profiletables.NetworkProfile
|
||||
template_name = 'router/nexus1000v/network_profile/index.html'
|
||||
|
||||
def get_data(self):
|
||||
return _get_profiles(self.request, 'network')
|
||||
|
||||
|
||||
class PolicyProfileIndexView(tables.DataTableView):
|
||||
table_class = profiletables.PolicyProfile
|
||||
template_name = 'router/nexus1000v/policy_profile/index.html'
|
||||
|
||||
def get_data(self):
|
||||
return _get_profiles(self.request, 'policy')
|
||||
|
||||
|
||||
class IndexTabGroup(tabs.TabGroup):
|
||||
slug = "group"
|
||||
tabs = (NetworkProfileIndexView, PolicyProfileIndexView,)
|
||||
|
||||
|
||||
class IndexView(tables.MultiTableView):
|
||||
table_classes = (profiletables.NetworkProfile,
|
||||
profiletables.PolicyProfile,)
|
||||
template_name = 'router/nexus1000v/index.html'
|
||||
|
||||
def get_network_profile_data(self):
|
||||
return _get_profiles(self.request, 'network')
|
||||
|
||||
def get_policy_profile_data(self):
|
||||
return _get_profiles(self.request, 'policy')
|
||||
|
||||
|
||||
class CreateNetworkProfileView(forms.ModalFormView):
|
||||
form_class = profileforms.CreateNetworkProfile
|
||||
template_name = 'router/nexus1000v/create_network_profile.html'
|
||||
success_url = urlresolvers.reverse_lazy('horizon:router:nexus1000v:index')
|
||||
|
||||
|
||||
class UpdateNetworkProfileView(forms.ModalFormView):
|
||||
form_class = profileforms.UpdateNetworkProfile
|
||||
template_name = 'router/nexus1000v/update_network_profile.html'
|
||||
context_object_name = 'network_profile'
|
||||
success_url = urlresolvers.reverse_lazy('horizon:router:nexus1000v:index')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UpdateNetworkProfileView,
|
||||
self).get_context_data(**kwargs)
|
||||
context["profile_id"] = self.kwargs['profile_id']
|
||||
return context
|
||||
|
||||
def _get_object(self, *args, **kwargs):
|
||||
if not hasattr(self, "_object"):
|
||||
profile_id = self.kwargs['profile_id']
|
||||
try:
|
||||
self._object = api.neutron.profile_get(self.request,
|
||||
profile_id)
|
||||
LOG.debug(_("Network Profile object=%s") % self._object)
|
||||
except Exception:
|
||||
redirect = self.success_url
|
||||
msg = _('Unable to retrieve network profile details.')
|
||||
exceptions.handle(self.request, msg, redirect=redirect)
|
||||
return self._object
|
||||
|
||||
def get_initial(self):
|
||||
profile = self._get_object()
|
||||
return {'profile_id': profile['id'],
|
||||
'name': profile['name'],
|
||||
'segment_range': profile['segment_range'],
|
||||
'segment_type': profile['segment_type'],
|
||||
'physical_network': profile['physical_network']}
|
@ -160,6 +160,11 @@ OPENSTACK_NEUTRON_NETWORK = {
|
||||
'enable_lb': False,
|
||||
'enable_quotas': True,
|
||||
'enable_security_group': True,
|
||||
# The profile_support option is used to detect if an external router can be
|
||||
# configured via the dashboard. When using specific plugins the
|
||||
# profile_support can be turned on if needed.
|
||||
'profile_support': None,
|
||||
#'profile_support': 'cisco',
|
||||
}
|
||||
|
||||
# The OPENSTACK_IMAGE_BACKEND settings can be used to customize features
|
||||
|
@ -54,7 +54,7 @@ STATIC_URL = '/static/'
|
||||
ROOT_URLCONF = 'openstack_dashboard.urls'
|
||||
|
||||
HORIZON_CONFIG = {
|
||||
'dashboards': ('project', 'admin', 'settings',),
|
||||
'dashboards': ('project', 'admin', 'settings', 'router',),
|
||||
'default_dashboard': 'project',
|
||||
'user_home': 'openstack_dashboard.views.get_user_home',
|
||||
'ajax_queue_limit': 10,
|
||||
@ -139,6 +139,7 @@ INSTALLED_APPS = (
|
||||
'openstack_dashboard.dashboards.admin',
|
||||
'openstack_dashboard.dashboards.settings',
|
||||
'openstack_auth',
|
||||
'openstack_dashboard.dashboards.router',
|
||||
)
|
||||
|
||||
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
|
||||
|
@ -37,6 +37,13 @@ INSTALLED_APPS = (
|
||||
'openstack_dashboard.dashboards.project',
|
||||
'openstack_dashboard.dashboards.admin',
|
||||
'openstack_dashboard.dashboards.settings',
|
||||
# If the profile_support config is turned on in local_settings
|
||||
# the "router" dashboard will be enabled which can be used to
|
||||
# create and use profiles with networks and instances. In which case
|
||||
# using run_tests will require the registration of the "router" dashboard.
|
||||
# TODO (absubram): Need to make this permanent when a better solution
|
||||
# for run_tests is implemented to use with and without the n1k sub-plugin.
|
||||
#'openstack_dashboard.dashboards.router',
|
||||
)
|
||||
|
||||
AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
|
||||
@ -44,7 +51,15 @@ AUTHENTICATION_BACKENDS = ('openstack_auth.backend.KeystoneBackend',)
|
||||
SITE_BRANDING = 'OpenStack'
|
||||
|
||||
HORIZON_CONFIG = {
|
||||
'dashboards': ('project', 'admin', 'settings'),
|
||||
'dashboards': ('project', 'admin', 'settings',),
|
||||
# If the profile_support config is turned on in local_settings
|
||||
# the "router" dashboard will be enabled which can be used to
|
||||
# create and use profiles with networks and instances. In which case
|
||||
# using run_tests will require the registration of the "router" dashboard.
|
||||
# TODO (absubram): Need to make this permanent when a better solution
|
||||
# for run_tests is implemented to use with and without the n1k sub-plugin.
|
||||
#'openstack_dashboard.dashboards.router',
|
||||
#'dashboards': ('project', 'admin', 'settings', 'router',),
|
||||
'default_dashboard': 'project',
|
||||
"password_validator": {
|
||||
"regex": '^.{8,18}$',
|
||||
|
@ -37,6 +37,10 @@ def data(TEST):
|
||||
TEST.members = utils.TestDataContainer()
|
||||
TEST.monitors = utils.TestDataContainer()
|
||||
TEST.neutron_quotas = utils.TestDataContainer()
|
||||
TEST.net_profiles = utils.TestDataContainer()
|
||||
TEST.policy_profiles = utils.TestDataContainer()
|
||||
TEST.network_profile_binding = utils.TestDataContainer()
|
||||
TEST.policy_profile_binding = utils.TestDataContainer()
|
||||
|
||||
# data return by neutronclient
|
||||
TEST.api_agents = utils.TestDataContainer()
|
||||
@ -52,6 +56,10 @@ def data(TEST):
|
||||
TEST.api_members = utils.TestDataContainer()
|
||||
TEST.api_monitors = utils.TestDataContainer()
|
||||
TEST.api_extensions = utils.TestDataContainer()
|
||||
TEST.api_net_profiles = utils.TestDataContainer()
|
||||
TEST.api_policy_profiles = utils.TestDataContainer()
|
||||
TEST.api_network_profile_binding = utils.TestDataContainer()
|
||||
TEST.api_policy_profile_binding = utils.TestDataContainer()
|
||||
|
||||
#------------------------------------------------------------
|
||||
# 1st network
|
||||
@ -85,6 +93,44 @@ def data(TEST):
|
||||
TEST.networks.add(neutron.Network(network))
|
||||
TEST.subnets.add(subnet)
|
||||
|
||||
# network profile for network when using the cisco n1k plugin
|
||||
net_profile_dict = {'name': 'net_profile_test1',
|
||||
'segment_type': 'vlan',
|
||||
'physical_network': 'phys1',
|
||||
'segment_range': '3000-31000',
|
||||
'id':
|
||||
'00000000-1111-1111-1111-000000000000',
|
||||
'tenant_id': network_dict['tenant_id']}
|
||||
|
||||
TEST.api_net_profiles.add(net_profile_dict)
|
||||
TEST.net_profiles.add(neutron.Profile(net_profile_dict))
|
||||
|
||||
# policy profile for port when using the cisco n1k plugin
|
||||
policy_profile_dict = {'name': 'policy_profile_test1',
|
||||
'id':
|
||||
'00000000-9999-9999-9999-000000000000'}
|
||||
|
||||
TEST.api_policy_profiles.add(policy_profile_dict)
|
||||
TEST.policy_profiles.add(neutron.Profile(policy_profile_dict))
|
||||
|
||||
# network profile binding
|
||||
network_profile_binding_dict = {'profile_id':
|
||||
'00000000-1111-1111-1111-000000000000',
|
||||
'tenant_id': network_dict['tenant_id']}
|
||||
|
||||
TEST.api_network_profile_binding.add(network_profile_binding_dict)
|
||||
TEST.network_profile_binding.add(neutron.Profile(
|
||||
network_profile_binding_dict))
|
||||
|
||||
# policy profile binding
|
||||
policy_profile_binding_dict = {'profile_id':
|
||||
'00000000-9999-9999-9999-000000000000',
|
||||
'tenant_id': network_dict['tenant_id']}
|
||||
|
||||
TEST.api_policy_profile_binding.add(policy_profile_binding_dict)
|
||||
TEST.policy_profile_binding.add(neutron.Profile(
|
||||
policy_profile_binding_dict))
|
||||
|
||||
# ports on 1st network
|
||||
port_dict = {'admin_state_up': True,
|
||||
'device_id': 'af75c8e5-a1cc-4567-8d04-44fcd6922890',
|
||||
|
Loading…
x
Reference in New Issue
Block a user