Cisco Standalone fabric horizon GUI

Extended Horizon Cisco UI for supporting Network Configuration Profile
for Nexus Programmable Fabric

Change-Id: I66381ec1a04b098eed27101dc55551776c8a1e3a
This commit is contained in:
chiragtayal 2016-08-15 17:46:50 -07:00
parent e3952d15e6
commit 8c630c4765
7 changed files with 342 additions and 0 deletions

View File

View File

@ -0,0 +1,92 @@
# Copyright 2014 Cisco Systems, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import logging
import platform
import sys
from networking_cisco.apps.saf.common import config
from networking_cisco.apps.saf.common import constants
from networking_cisco.apps.saf.common import rpc
from horizon import exceptions
LOG = logging.getLogger(__name__)
class DFAClient(object):
"""Represents fabric enabler command line interface."""
def __init__(self):
self.ctl_host = platform.node()
self._cfg = config.CiscoDFAConfig().cfg
url = self._cfg.dfa_rpc.transport_url % ({'ip': self.ctl_host})
self.clnt = rpc.DfaRpcClient(url, constants.DFA_SERVER_QUEUE,
exchange=constants.DFA_EXCHANGE)
def do_precreate_network(self, network):
'''Precreate network on current version of Fabric Enabler'''
context = {}
args = json.dumps(network)
msg = self.clnt.make_msg('precreate_network', context, msg=args)
try:
resp = self.clnt.call(msg)
if not resp:
raise exceptions.NotAvailable("Project %(id)s not present in "
"fabric enabler" %
{'id': network.get('tenant_id')})
return resp
except (rpc.MessagingTimeout, rpc.RPCException, rpc.RemoteError):
LOG.error("RPC: Request to precreate network failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
def do_delete_precreate_network(self, network):
'''Delete precreate network on current version of Fabric Enabler'''
context = {}
args = json.dumps(network)
msg = self.clnt.make_msg('delete_precreate_network', context, msg=args)
try:
resp = self.clnt.call(msg)
if not resp:
raise exceptions.NotAvailable("Project %(id)s not present in "
"fabric enabler" %
{'id': network.get('tenant_id')})
return resp
except (rpc.MessagingTimeout, rpc.RPCException, rpc.RemoteError):
LOG.error("RPC: Request to delete precreated network failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
def get_config_profiles_detail(self):
'''Get all config Profiles details from the Fabric Enabler'''
context = {}
args = json.dumps({})
msg = self.clnt.make_msg('get_config_profiles_detail',
context, msg=args)
try:
resp = self.clnt.call(msg)
return resp
except (rpc.MessagingTimeout, rpc.RPCException, rpc.RemoteError):
LOG.error("RPC: Request for detailed Config Profiles failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
def dfa_client():
sys.argv.append('--config-file')
sys.argv.append('/etc/saf/enabler_conf.ini')
return DFAClient()

View File

@ -0,0 +1,83 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import mock
import platform
from horizon_cisco_ui.cisco.dfa import dfa_client
from networking_cisco.apps.saf.common import config
from networking_cisco.apps.saf.common import constants
from openstack_dashboard.test import helpers as test
class DFAClientTestCase(test.BaseAdminViewTests):
def setUp(self):
'''Setup routine '''
super(DFAClientTestCase, self).setUp()
self.ip = platform.node()
self._test_dfa_client_init()
def _test_dfa_client_init(self):
with mock.patch('networking_cisco.apps.saf.common.rpc.DfaRpcClient') as dfa_rpc, \
mock.patch.object(config.CiscoDFAConfig, 'cfg') as dfa_cfg:
self.DFAClient = dfa_client.DFAClient()
self.url = dfa_cfg.dfa_rpc.transport_url % ({'ip': self.ip})
dfa_rpc.assert_called_with(self.url,
constants.DFA_SERVER_QUEUE,
exchange=constants.DFA_EXCHANGE)
def test_do_precreate_network(self):
network = dict(tenant_id=1,
nwk_name='net1',
subnet='10.0.0.0/24',
cfgp='defaultNetworkL2Profile')
message = json.dumps(network)
with mock.patch.object(self.DFAClient.clnt, 'make_msg') as mock_make_msg, \
mock.patch.object(self.DFAClient.clnt, 'call') as mock_call:
self.DFAClient.do_precreate_network(network)
rpc_make_obj = mock_make_msg.return_value
mock_make_msg.assert_called_with('precreate_network', {}, msg=message)
mock_call.assert_called_with(rpc_make_obj)
def test_do_precreate_network_not_available_exception(self):
network = dict(tenant_id=1,
nwk_name='net1',
subnet='10.0.0.0/24',
cfgp='defaultNetworkL2Profile')
with mock.patch.object(self.DFAClient.clnt, 'call',
return_value=False), \
self.assertRaises(dfa_client.exceptions.NotAvailable) as cm:
self.DFAClient.do_precreate_network(network)
self.assertEqual('Project 1 not present in fabric enabler',
str(cm.exception))
def test_do_precreate_network_rpc_exception(self):
network = dict(tenant_id=1,
nwk_name='net1',
subnet='10.0.0.0/24',
cfgp='defaultNetworkL2Profile')
with mock.patch.object(self.DFAClient.clnt, 'call',
side_effect=dfa_client.rpc.RPCException), \
self.assertRaises(dfa_client.exceptions.NotAvailable) as cm:
self.DFAClient.do_precreate_network(network)
self.assertEqual('RPC to Fabric Enabler failed',
str(cm.exception))

View File

@ -0,0 +1,154 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from django.utils.translation import ugettext_lazy as _
import logging
from horizon import exceptions
from horizon import forms
from horizon import workflows
from horizon_cisco_ui.cisco.dfa import dfa_client
from openstack_dashboard import api
from openstack_dashboard.dashboards.project.networks import \
workflows as upstream_networks_workflows
LOG = logging.getLogger(__name__)
class DFAConfigProfileAction(workflows.Action):
cfg_profile = forms.ChoiceField(label=_("Config Profile"), required=False,
help_text=_("Select Config Profile to "
"associate with Network."))
def __init__(self, request, *args, **kwargs):
super(DFAConfigProfileAction, self).__init__(request, *args, **kwargs)
self.fields['cfg_profile'].choices = \
self.get_config_profile_choices(request)
def get_config_profile_choices(self, request):
profile_choices = [('', _("Select a config profile"))]
for profile in self._get_cfg_profiles(request):
profile_choices.append((profile, profile))
return profile_choices
def _get_cfg_profiles(self, request):
profiles = []
try:
cfgplist = dfa_client.dfa_client().get_config_profiles_detail()
profiles = [profile for cfgp in cfgplist
if (cfgp.get('profileSubType') == 'network:universal')
for profile in [cfgp.get('profileName')]]
except Exception as exc:
exceptions.handle(request, exc.message)
return profiles
class Meta(object):
name = _("Programmable Fabric")
help_text = _("Select Config Profile from the list "
"to associate with your network.")
class DFAConfigProfileInfo(workflows.Step):
action_class = DFAConfigProfileAction
def contribute(self, data, context):
for key, value in data.items():
context[key] = value
return context
class DFACreateNetwork(upstream_networks_workflows.CreateNetwork):
default_steps = (upstream_networks_workflows.CreateNetworkInfo,
upstream_networks_workflows.CreateSubnetInfo,
upstream_networks_workflows.CreateSubnetDetail,
DFAConfigProfileInfo)
@staticmethod
def _create_network(self, request, data):
try:
params = {'name': data['net_name'],
'admin_state_up': (data['admin_state'] == 'True'),
'shared': data['shared']}
if api.neutron.is_port_profiles_supported():
params['net_profile_id'] = data['net_profile_id']
network = api.neutron.network_create(request, **params)
self.context['net_id'] = network.id
msg = (_('Network "%s" was successfully created.') %
network.name_or_id)
LOG.debug(msg)
return network
except Exception as e:
msg = (_('Failed to create network "%(network)s": %(reason)s') %
{"network": data['net_name'], "reason": e})
LOG.info(msg)
# Delete Precreated Network
try:
precreate_network = dict(tenant_id=request.user.project_id,
nwk_name=data['net_name'],
subnet=data['cidr'],
cfgp=data['cfg_profile'])
dfaclient = dfa_client.dfa_client()
dfaclient.do_delete_precreate_network(precreate_network)
except Exception as exc:
LOG.error('Unable to delete precreated Network')
exceptions.handle(self.request, exc.message)
redirect = self.get_failure_url()
exceptions.handle(request, msg, redirect=redirect)
return False
@staticmethod
def handle(self, request, data):
precreate_flag = False
'''Pre-create network in enabler'''
precreate_network = dict(tenant_id=request.user.project_id,
nwk_name=data['net_name'],
subnet=data['cidr'],
cfgp=data['cfg_profile'])
if data['cfg_profile']:
dfaclient = dfa_client.dfa_client()
try:
precreate_flag = dfaclient.do_precreate_network(
precreate_network)
except Exception as exc:
LOG.error('Unable to do precreate Network')
exceptions.handle(self.request, exc.message)
return False
network = self._create_network(request, data)
if not network:
if precreate_flag:
# do precreate delete
try:
dfaclient.do_delete_precreate_network(precreate_network)
precreate_flag = False
except Exception as exc:
LOG.error('Unable to delete precreatedd Network')
exceptions.handle(self.request, exc.message)
return False
# If we do not need to create a subnet, return here.
if not data['with_subnet']:
return True
subnet = self._create_subnet(request, data, network, no_redirect=True)
if subnet:
return True
else:
self._delete_network(request, network)
if precreate_flag:
# do precreate delete
try:
dfaclient.do_delete_precreate_network(precreate_network)
except Exception as exc:
LOG.error('Unable to delete precreated Network')
exceptions.handle(self.request, exc.message)
return False

View File

@ -13,10 +13,22 @@
# under the License.
from horizon_cisco_ui.cisco.dfa \
import workflows as cisco_network_workflows
from horizon_cisco_ui.firewalls \
import views as cisco_firewall_views
from openstack_dashboard.dashboards.project.firewalls \
import views as firewall_views
from openstack_dashboard.dashboards.project.networks \
import workflows as horizon_network_workflows
horizon_network_workflows.CreateNetwork.default_steps = \
cisco_network_workflows.DFACreateNetwork.default_steps
horizon_network_workflows.CreateNetwork.handle = \
cisco_network_workflows.DFACreateNetwork.handle
horizon_network_workflows.CreateNetwork._create_network = \
cisco_network_workflows.DFACreateNetwork._create_network
# TODO(robcresswell): remove example
# Silly example that does nothing, but illustrates an override.

View File

@ -17,6 +17,7 @@ setenv = VIRTUAL_ENV={envdir}
PYTHONHASHSEED=0
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
networking-cisco
commands = {toxinidir}/manage.py test horizon_cisco_ui --settings=horizon_cisco_ui.test.settings
[testenv:pep8]