Merge "Programmable Fabric Panel in Cisco dashboard"

This commit is contained in:
Jenkins 2017-02-03 13:59:51 +00:00 committed by Gerrit Code Review
commit 9fe4a0f8b5
26 changed files with 1333 additions and 44 deletions

View File

@ -1,2 +1,3 @@
Rob Cresswell <robert.cresswell@outlook.com>
Saksham Varma <sakvarma@cisco.com>
Chirag Tayal <chiragtayal@gmail.com>

View File

@ -14,12 +14,10 @@
# under the License.
import ConfigParser
import json
import logging
import platform
from horizon import exceptions
from horizon.utils.memoized import memoized
from django.utils.translation import ugettext_lazy as _
try:
import oslo_messaging as messaging
@ -30,11 +28,14 @@ try:
except ImportError:
from oslo.config import cfg
from horizon import exceptions
from horizon.utils.memoized import memoized
LOG = logging.getLogger(__name__)
class DFAClient(object):
"""Represents fabric enabler command line interface."""
"""Represents Nexus Fabric Enabler RPC Client."""
def __init__(self):
self.setup_client()
@ -58,30 +59,144 @@ class DFAClient(object):
return self.clnt
def associate_profile_with_network(self, network):
context = {}
args = json.dumps(network)
try:
resp = self.clnt.call(context,
'associate_profile_with_network',
msg=args)
return resp
except (messaging.MessagingException, messaging.RemoteError,
messaging.MessagingTimeout):
LOG.error("RPC: Request to associate profile with 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'''
@memoized
def dfaclient():
return DFAClient().clnt
context = {}
args = json.dumps({})
try:
resp = self.clnt.call(context, 'get_config_profiles_detail',
msg=args)
return resp
except (messaging.MessagingException, messaging.RemoteError,
messaging.MessagingTimeout):
LOG.error("RPC: Request for detailed Config Profiles failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
def associate_profile_with_network(network):
'''Associate Network Profile with network'''
try:
resp = dfaclient().call({}, 'associate_profile_with_network',
msg=network)
return resp
except (messaging.MessagingException, messaging.RemoteError,
messaging.MessagingTimeout):
LOG.error("RPC: Request to associate_profile_with_network failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
def do_associate_dci_id_to_project(tenant):
'''Associate DCI ID to Project'''
try:
resp = dfaclient().cast({}, 'associate_dci_id_to_project',
msg=tenant)
return resp
except (messaging.MessagingException, messaging.RemoteError,
messaging.MessagingTimeout):
LOG.error("RPC: Request to associate DCI_ID to Project failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
def get_fabric_summary():
'''Get fabric details from the Fabric Enabler'''
try:
resp = dfaclient().call({}, 'get_fabric_summary', msg={})
return resp
except (messaging.MessagingException, messaging.RemoteError,
messaging.MessagingTimeout):
LOG.error("RPC: Request for Fabric Summary failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
def get_per_config_profile_detail(cfg_profile):
'''Get all config Profiles details from the Fabric Enabler'''
try:
resp = dfaclient().call({}, 'get_per_config_profile_detail',
msg=cfg_profile)
return resp
except Exception as e:
mess = (_('%(reason)s') % {"reason": e})
LOG.error(mess)
reason = mess.partition("Traceback")[0]
raise exceptions.NotAvailable(reason)
def get_config_profiles_detail():
'''Get all config Profiles details from the Fabric Enabler'''
try:
resp = dfaclient().call({}, 'get_config_profiles_detail', msg={})
return resp
except Exception as e:
mess = (_('%(reason)s') % {"reason": e})
reason = mess.partition("Traceback")[0]
raise exceptions.NotAvailable(reason)
def get_project_details(tenant):
'''Get project details for a tenant from the Fabric Enabler'''
try:
resp = dfaclient().call({}, 'get_project_detail', msg=tenant)
if not resp:
raise exceptions.NotFound("Project Not Found in Fabric \
Enabler")
return resp
except (messaging.MessagingException, messaging.RemoteError,
messaging.MessagingTimeout):
LOG.error("RPC: Request for project details failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
def get_network_by_tenant_id(tenant):
'''Get all networks for a tenant from the Fabric Enabler'''
try:
resp = dfaclient().call({}, 'get_all_networks_for_tenant', msg=tenant)
if resp is False:
raise exceptions.NotFound("Project Not Found in Fabric \
Enabler")
return resp
except (messaging.MessagingException, messaging.RemoteError,
messaging.MessagingTimeout):
LOG.error("RPC: Request for project details failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
def get_instance_by_tenant_id(tenant):
'''Get all instances for a tenant from the Fabric Enabler'''
try:
resp = dfaclient().call({}, 'get_instance_by_tenant_id', msg=tenant)
if resp is False:
raise exceptions.NotFound("Project Not Found in Fabric \
Enabler")
return resp
except (messaging.MessagingException, messaging.RemoteError,
messaging.MessagingTimeout):
LOG.error("RPC: Request for project details failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
def get_agents_details():
'''Get all Enabler agents from the Fabric Enabler'''
try:
resp = dfaclient().call({}, 'get_agents_details', msg={})
if not resp:
raise exceptions.NotFound("No Agents found for Fabric Enabler")
return resp
except (messaging.MessagingException, messaging.RemoteError,
messaging.MessagingTimeout):
LOG.error("RPC: Request for project details failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
def get_agent_details_per_host(agent):
'''Get Enabler agent for a host from the Fabric Enabler'''
try:
resp = dfaclient().call({}, 'get_agent_details_per_host', msg=agent)
if not resp:
raise exceptions.NotFound("No Agent found for Fabric Enabler")
return resp
except (messaging.MessagingException, messaging.RemoteError,
messaging.MessagingTimeout):
LOG.error("RPC: Request for project details failed.")
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")

View File

@ -0,0 +1,63 @@
# 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 logging
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import forms
from horizon_cisco_ui.cisco.dfa import dfa_client
LOG = logging.getLogger(__name__)
class AssociateDCI(forms.SelfHandlingForm):
project_id = forms.CharField(label=_("Project ID "),
widget=forms.TextInput(
attrs={'readonly': 'readonly'}))
dci_id = forms.IntegerField(label=_("DCI ID"), min_value=1,
max_value=1600000)
def handle(self, request, data):
try:
LOG.debug('request = %(req)s, params = %(params)s',
{'req': request, 'params': data})
tenant = dict(tenant_id=request.user.project_id,
tenant_name=request.user.project_name,
dci_id=data['dci_id'])
dfa_client.do_associate_dci_id_to_project(tenant)
except Exception:
redirect = reverse('horizon:cisco:dfa:index')
msg = _('Failed to associate DCI ID to project')
exceptions.handle(request, msg, redirect=redirect)
return True
class DisassociateDCI(forms.SelfHandlingForm):
def handle(self, request, data):
try:
LOG.debug('request = %(req)s, params = %(params)s',
{'req': request, 'params': data})
tenant = dict(tenant_id=request.user.project_id,
tenant_name=request.user.project_name,
dci_id=0)
dfa_client.do_associate_dci_id_to_project(tenant)
except Exception:
redirect = reverse('horizon:cisco:dfa:index')
msg = _('Failed to disassociate DCI ID from project')
exceptions.handle(request, msg, redirect=redirect)
return True

View File

@ -0,0 +1,43 @@
# 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 horizon
from horizon_cisco_ui.cisco import dashboard
import logging
import os.path
LOG = logging.getLogger(__name__)
class DFA(horizon.Panel):
name = _("Programmable Fabric")
slug = "dfa"
permissions = ('openstack.services.network',)
def allowed(self, context):
request = context['request']
if not request.user.has_perms(self.permissions):
return False
try:
if not os.path.isfile('/etc/saf/enabler_conf.ini'):
return False
except Exception:
LOG.error("Exception occured trying to find the Nexus Fabric "
"Enabler Configuration File")
return False
if not super(DFA, self).allowed(context):
return False
return True
dashboard.Cisco.register(DFA)

View File

@ -0,0 +1,234 @@
# 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 import template
from django.utils.translation import ugettext_lazy as _
from horizon import tables
import logging
LOG = logging.getLogger(__name__)
class AssociateDCIAction(tables.LinkAction):
name = "associate"
verbose_name = _("Associate DCI ID")
url = "horizon:cisco:dfa:associate"
classes = ("ajax-modal",)
icon = "link"
class DisassociateDCIAction(tables.LinkAction):
name = "disassociate"
verbose_name = _("Disassociate DCI ID")
url = "horizon:cisco:dfa:disassociate"
classes = ("ajax-modal",)
icon = "link"
class SearchFilterAction(tables.FilterAction):
name = "searchfilter"
def get_vdp(obj):
if obj.get('vdp_vlan') > 0:
return obj.get('vdp_vlan')
if 'reason' in obj:
template_name = 'cisco/dfa/_vdp_reason.html'
context = {
"vdp": obj.get('vdp_vlan'),
"id": obj.get('port_id'),
"reason": obj.get('reason')
}
return template.loader.render_to_string(template_name, context)
return obj.get('vdp_vlan')
def get_result(obj):
if obj.get('result') == 'SUCCESS':
return obj.get('result')
if 'network_id' in obj:
obj_id = obj.get('network_id')
elif 'project_id' in obj:
obj_id = obj.get('project_id')
elif 'port_id' in obj:
obj_id = obj.get('port_id')
else:
obj_id = 'none'
if 'reason' in obj:
template_name = 'cisco/dfa/_reason.html'
context = {
"result": obj.get('result'),
"id": obj_id,
"reason": obj.get('reason')
}
return template.loader.render_to_string(template_name, context)
return obj.get('result')
def get_instance_info(obj):
if 'instance_id' and 'veth_intf' in obj:
template_name = 'cisco/dfa/_instance.html'
context = {
"name": obj.get('name'),
"nw_name": obj.get('network_name'),
"id": ''.join(e for e in obj.get('instance_id') if e.isalnum()),
"mac": obj.get('mac'),
"ip": obj.get('ip'),
"port": obj.get('port_id'),
"host_port": ', '.join((obj.get('host'), obj.get('veth_intf'))),
"TOR_port": ', '.join((obj.get('remote_system_name'),
obj.get('remote_port')))
}
return template.loader.render_to_string(template_name, context)
return obj.get('name')
class FabricSummaryTable(tables.DataTable):
key = tables.Column("key", sortable=False,
verbose_name=_("Fabric Property"))
value = tables.Column("value",
verbose_name=_("Value"))
def get_object_id(self, obj):
return obj.get('key')
class Meta(object):
name = "fabricsummary"
verbose_name = _("Fabric Summary")
table_actions = (SearchFilterAction, )
multi_select = False
class CFGProfileTable(tables.DataTable):
profile_name = tables.Column("profileName", sortable=False,
verbose_name=_("Profile Name"),
link='horizon:cisco:dfa:detailprofile')
def get_object_id(self, obj):
return ':'.join((obj.get('profileName'), obj.get('profileType')))
class Meta(object):
name = "cfgprofile"
verbose_name = _("CFGProfile")
table_actions = (SearchFilterAction, )
multi_select = False
class ProjectTable(tables.DataTable):
project_name = tables.Column("project_name",
verbose_name=_("Project Name"))
project_id = tables.Column("project_id", verbose_name=_("Project ID"))
seg_id = tables.Column("seg_id", verbose_name=_("L3 VNI"))
dci_id = tables.Column("dci_id", verbose_name=_("DCI ID"))
result = tables.Column(get_result, verbose_name=_("Result"))
def get_object_id(self, obj):
return obj.get('project_id')
class Meta(object):
name = "projecttable"
hidden_title = False
verbose_name = _("Projects")
row_actions = (AssociateDCIAction, DisassociateDCIAction, )
class NetworkTable(tables.DataTable):
network_name = tables.Column("network_name", verbose_name=_("Name"))
network_id = tables.Column("network_id", verbose_name=_("ID"))
config_profile = tables.Column("config_profile",
verbose_name=_("Network Profile"))
seg_id = tables.Column("seg_id", verbose_name=_("Segmentation ID"))
vlan_id = tables.Column("vlan_id", verbose_name=_("Vlan ID"))
result = tables.Column(get_result, verbose_name=_("Result"))
def get_object_id(self, obj):
return obj.get('network_id')
class Meta(object):
name = "networktable"
hidden_title = False
verbose_name = _("Networks")
table_actions = (SearchFilterAction,)
multi_select = False
class InstanceTable(tables.DataTable):
instance_name = tables.Column(get_instance_info, verbose_name=_("Name"))
host = tables.Column("host", verbose_name=_("Host"))
tor = tables.Column("remote_system_name", verbose_name=_("TOR"))
network_name = tables.Column("network_name",
verbose_name=_("Network Name"))
local_vlan = tables.Column("local_vlan", verbose_name=_("Local Vlan"))
vdp_vlan = tables.Column(get_vdp, verbose_name=_("Link Local Vlan"))
seg_id = tables.Column("seg_id", verbose_name=_("Segmentation ID"))
result = tables.Column(get_result, verbose_name=_("Result"))
def get_object_id(self, obj):
return obj.get('port_id')
class Meta(object):
name = "instancetable"
hidden_title = False
verbose_name = _("Instances")
table_actions = (SearchFilterAction,)
multi_select = False
class AgentsTable(tables.DataTable):
host = tables.Column("host", verbose_name=_("Host"),
link='horizon:cisco:dfa:detail')
created = tables.Column("created", verbose_name=_("Created"))
heartbeat = tables.Column("heartbeat", verbose_name=_("Heartbeat"))
agent_status = tables.Column("agent_status",
verbose_name=_("Agent Status"))
def get_object_id(self, obj):
return obj.get('host')
class Meta(object):
name = "agentstable"
verbose_name = _("Agents Table")
table_actions = (SearchFilterAction,)
multi_select = False
class TopologyTable(tables.DataTable):
interface = tables.Column("interface", verbose_name=_("Interface"))
remote_port = tables.Column("remote_port", verbose_name=_("Remote Port"))
bond_intf = tables.Column("bond_intf", verbose_name=_("Bond Interface"))
remote_evb_cfgd = tables.Column("remote_evb_cfgd",
verbose_name=_("Remote EVB Configured"))
remote_system_desc = tables.Column("remote_system_desc",
verbose_name=_("Remote System"))
remote_chassis_mac = tables.Column("remote_chassis_mac",
verbose_name=_("Remote Chassis Mac"))
remote_mgmt_addr = tables.Column("remote_mgmt_addr",
verbose_name=_("Remote Mgmt Address"))
remote_system_name = tables.Column("remote_system_name",
verbose_name=_("Remote Sys Name"))
remote_evb_mode = tables.Column("remote_evb_mode",
verbose_name=_("Remote EVB Mode"))
def get_object_id(self, obj):
return obj.get('interface')
class Meta(object):
name = "topology"
hidden_title = False
verbose_name = _("Topology")
multi_select = False

View File

@ -0,0 +1,133 @@
# 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 dateutil.parser
import json
import logging
from django.utils.translation import ugettext_lazy as _
from horizon import exceptions
from horizon import tabs
from horizon_cisco_ui.cisco.dfa import dfa_client
from horizon_cisco_ui.cisco.dfa import tables
LOG = logging.getLogger(__name__)
class FabricSummaryTab(tabs.TableTab):
name = _("Fabric Summary")
slug = "fabric_summary_tab"
table_classes = (tables.FabricSummaryTable,)
template_name = ("horizon/common/_detail_table.html")
def get_fabricsummary_data(self):
summary = []
try:
summary = dfa_client.get_fabric_summary()
except Exception as exc:
exceptions.handle(self.request, exc.message)
return summary
class CFGProfileTab(tabs.TableTab):
name = _("Network Profiles")
slug = "cfgprofile_tab"
table_classes = (tables.CFGProfileTable,)
template_name = ("horizon/common/_detail_table.html")
def get_cfgprofile_data(self):
try:
cfgplist = dfa_client.get_config_profiles_detail()
profile_list = [p for p in cfgplist
if (p.get('profileSubType') ==
'network:universal')]
except Exception as exc:
profile_list = []
exceptions.handle(self.request, exc.message)
return profile_list
class NFEInfoTab(tabs.TableTab):
name = _("Fabric View")
slug = "nfe_info_tab"
table_classes = (tables.ProjectTable, tables.NetworkTable,
tables.InstanceTable, )
template_name = 'cisco/dfa/enablerinfo_tables.html'
def get_projecttable_data(self):
try:
tenant = dict(tenant_id=self.request.user.project_id)
project_list = dfa_client.get_project_details(tenant)
except Exception as exc:
project_list = []
exceptions.handle(self.request, exc.message)
return project_list
def get_networktable_data(self):
try:
tenant = dict(tenant_id=self.request.user.project_id)
netlist = dfa_client.get_network_by_tenant_id(tenant)
except Exception as exc:
netlist = []
exceptions.handle(self.request, exc.message)
return netlist
def get_instancetable_data(self):
try:
tenant = dict(tenant_id=self.request.user.project_id)
instance_list = dfa_client.get_instance_by_tenant_id(tenant)
agent_list = dfa_client.get_agents_details()
port = {}
for agent in agent_list:
cfg = json.loads(agent.get('config'))
topo = cfg.get('topo')
intf = topo.get(cfg.get('veth_intf'))
host = agent.get('host')
if not intf:
continue
port.update({host: dict(veth_intf=cfg.get('uplink'),
remote_system_name=intf.get('remote_system_name'),
remote_port=intf.get('remote_port'))})
if port:
for instance in instance_list:
instance.update(port.get(instance.get('host')))
except Exception as exc:
instance_list = []
exceptions.handle(self.request, exc.message)
return instance_list
class NFEAgentTab(tabs.TableTab):
name = _("Topology View")
slug = "nfe_agents_tab"
table_classes = (tables.AgentsTable,)
template_name = ("horizon/common/_detail_table.html")
def get_agentstable_data(self):
try:
agent_list = dfa_client.get_agents_details()
for agent in agent_list:
agent["heartbeat"] = dateutil.parser.parse(
agent.get('heartbeat'))
agent["created"] = dateutil.parser.parse(agent.get('created'))
except Exception as exc:
agent_list = []
exceptions.handle(self.request, exc.message)
return agent_list
class DFATabs(tabs.TabGroup):
slug = "dfa_tabs"
tabs = (FabricSummaryTab, CFGProfileTab, NFEInfoTab, NFEAgentTab)
sticky = True

View File

@ -0,0 +1,7 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<p><strong>{% trans "Data Center Interconnect:" %}</strong> {% blocktrans %} Cisco DCI solutions extend LAN and SAN connectivity across geographically dispersed active data centers.{% endblocktrans %}</p>
<p><strong>{% trans "DCI ID:" %}</strong> {% blocktrans %} 1 - 1600000{% endblocktrans %}</p>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% load i18n sizeformat parse_date %}
<div class="detail">
<dl class="dl-horizontal">
<dt>{% trans "Host Name" %}</dt>
<dd>{{ agent.host }}</dd>
<dt>{% trans "Created At" %}</dt>
<dd>{{ agent.created|parse_date }}</dd>
<dt>{% trans "Heartbeat" %}</dt>
<dd>{{ agent.heartbeat|parse_date }}</dd>
<dt>{% trans "Uplink" %}</dt>
<dd>{{ uplink |default:_("None")}}</dd>
<dt>{% trans "veth Interface" %}</dt>
<dd>{{ veth_intf |default:_("None")}}</dd>
<dt>{% trans "Member Ports" %}</dt>
<dd>{{ memb_ports |default:_("None")}}</dd>
</dl>
</div>

View File

@ -0,0 +1,18 @@
{% load i18n %}
<div class="detail">
<p> {% trans Network Profile is an autoconfiguration template consisting of collection of commands which instantiates day-1 tenant-related configurations on CISCO Nexus switches.<br> Profiles can be added or modified only from DCNM %}</p><br>
<dl class="dl-horizontal">
<dt>{% trans "Profile Name: " %}</dt>
<dd>{{ Profile_Name }}</dd>
<dt>{% trans "Profile Type: " %}</dt>
<dd>{{ Profile_Type }}</dd>
<dt>{% trans "Forwarding Mode: " %}</dt>
<dd>{{ fwding_mode }}</dd>
<dt>{% trans "Description: " %}</dt>
<dd>{{ description }}</dd><br>
<dt>{% trans "Commands: " %}</dt>
<dd>{% autoescape off %}{{ commands }}{% endautoescape %}</dd>
</dl>
</div>

View File

@ -0,0 +1,6 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body%}
<p>{% blocktrans %}Are you sure to dis-associate DCI ID to Project?{% endblocktrans %}</p>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% load i18n %}
<a href="#" id="instance_{{ id }}_{{ nw_name }}" class="link-popover" rel="popover" tabindex="0" data-placement="right" data-trigger="focus" data-content="
<table class='table table-bordered'>
<tr><th>{% trans 'IP' %}</th><td>{{ ip }}</td></tr>
<tr><th>{% trans 'MAC' %}</th><td>{{ mac }}</td></tr>
<tr><th>{% trans 'PORT' %}</th><td>{{ port }}</td></tr>
<tr><th>{% trans 'HOST, Port' %}</th><td>{{ host_port }}</td></tr>
<tr><th>{% trans 'TOR, Port' %}</th><td>{{ TOR_port }}</td></tr>
</table>
" data-original-title="{% blocktrans %}Instance Details: {{ name }}">{{ name }}{% endblocktrans %}</a>
<script type="text/javascript" charset="utf-8">
$(function () {
var $instance = $("#instance_{{ id }}_{{ nw_name }}");
if ( $instance.popover ) {
$instance.popover({html:true});
}
});
</script>

View File

@ -0,0 +1,4 @@
{% load i18n %}
<a href="#" data-toggle="tooltip" data-placement="right" data-trigger="focus" data-original-title="
{{ description }}
">{{ name }}</a>

View File

@ -0,0 +1,14 @@
{% load i18n %}
<a href="#" id="result_{{ result }}_{{ id }}" class="link-popover" rel="popover" tabindex="0" data-placement="left" data-trigger="focus" data-content="
<table class='table table-bordered'>
<tr><th>{% trans 'Reason' %}</th><td>{{ reason }}</td></tr>
</table>
" data-original-title="{% blocktrans %}">{{ result }}{% endblocktrans %}</a>
<script type="text/javascript" charset="utf-8">
$(function () {
var $result = $("#result_{{ result }}_{{ id }}");
if ( $result.popover ) {
$result.popover({html:true});
}
});
</script>

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Associate DCI ID to Project" %}{% endblock %}
{% block main %}
{% include "cisco/dfa/_associate.html" %}
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% load i18n breadcrumb_nav %}
{% block title %}{% trans "Fabric Enabler Hosts"%}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{% include "cisco/dfa/_detail_overview.html" %}
<hr>
<div id="topology">
{{ topology_table.render }}
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% load i18n breadcrumb_nav %}
{% block title %}{% trans "Network Profiles"%}{% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{% include "cisco/dfa/_detail_profile_overview.html" %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Disassociate DCI ID from Project" %}{% endblock %}
{% block main %}
{% include "cisco/dfa/_disassociate.html" %}
{% endblock %}

View File

@ -0,0 +1,14 @@
{% block main %}
<div id="project_id">
{{ projecttable_table.render }}
</div>
<div id="network">
{{ networktable_table.render }}
</div>
<div id="instance">
{{ instancetable_table.render }}
</div>
{% endblock %}

View File

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %} {% trans "DFA" %} {% endblock %}
{% block main %}
<div class="row">
<div class="col-sm-12">
{{ tab_group.render }}
</div>
</div>
{% endblock %}

View File

@ -1,3 +1,6 @@
# 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
@ -10,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
import mock
import platform
@ -38,12 +40,13 @@ class DFAClientTestCase(test.BaseAdminViewTests):
name='net1',
tenant_id=1)
message = json.dumps(network)
with mock.patch.object(self.client.clnt, 'call') as mock_call:
self.client.associate_profile_with_network(network)
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as \
mock_client:
mock_client.return_value.call = mock.MagicMock()
dfa_client.associate_profile_with_network(network)
mock_call.assert_called_with({}, 'associate_profile_with_network',
msg=message)
mock_client.return_value.call.assert_called_with(
{}, 'associate_profile_with_network', msg=network)
def test_associate_profile_with_network_rpc_exception(self):
network = dict(id='1125-as45-afg5f-3457',
@ -51,10 +54,97 @@ class DFAClientTestCase(test.BaseAdminViewTests):
name='net1',
tenant_id=1)
with mock.patch.object(self.client.clnt, 'call',
side_effect=dfa_client.messaging.MessagingException), \
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as mock_client, \
self.assertRaises(dfa_client.exceptions.NotAvailable) as cm:
self.client.associate_profile_with_network(network)
mock_client.return_value.call = mock.MagicMock(
side_effect=dfa_client.messaging.MessagingException)
dfa_client.associate_profile_with_network(network)
self.assertEqual('RPC to Fabric Enabler failed',
str(cm.exception))
def test_do_associate_dci_id_to_project(self):
tenant = dict(tenant_id=1,
tenant_name='Project1',
dci_id=1001)
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as \
mock_client:
mock_client.return_value.cast = mock.MagicMock()
dfa_client.do_associate_dci_id_to_project(tenant)
mock_client.return_value.cast.assert_called_with(
{}, 'associate_dci_id_to_project', msg=tenant)
def test_get_fabric_summary(self):
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as \
mock_client:
mock_client.return_value.call = mock.MagicMock()
resp = dfa_client.get_fabric_summary()
mock_client.return_value.call.assert_called_with(
{}, 'get_fabric_summary', msg={})
self.assertEqual(resp, mock_client.return_value.call.return_value)
def test_get_config_profiles_detail(self):
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as \
mock_client:
mock_client.return_value.call = mock.MagicMock()
resp = dfa_client.get_config_profiles_detail()
mock_client.return_value.call.assert_called_with(
{}, 'get_config_profiles_detail', msg={})
self.assertEqual(resp, mock_client.return_value.call.return_value)
def test_get_project_details(self):
tenant = dict(tenant_id=1)
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as \
mock_client:
mock_client.return_value.call = mock.MagicMock()
resp = dfa_client.get_project_details(tenant)
mock_client.return_value.call.assert_called_with(
{}, 'get_project_detail', msg=tenant)
self.assertEqual(resp, mock_client.return_value.call.return_value)
def test_get_network_by_tenant_id(self):
tenant = dict(tenant_id=1)
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as \
mock_client:
mock_client.return_value.call = mock.MagicMock()
resp = dfa_client.get_network_by_tenant_id(tenant)
mock_client.return_value.call.assert_called_with(
{}, 'get_all_networks_for_tenant', msg=tenant)
self.assertEqual(resp, mock_client.return_value.call.return_value)
def test_get_instance_by_tenant_id(self):
tenant = dict(tenant_id=1)
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as \
mock_client:
mock_client.return_value.call = mock.MagicMock()
resp = dfa_client.get_instance_by_tenant_id(tenant)
mock_client.return_value.call.assert_called_with(
{}, 'get_instance_by_tenant_id', msg=tenant)
self.assertEqual(resp, mock_client.return_value.call.return_value)
def test_get_agents_details(self):
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as \
mock_client:
mock_client.return_value.call = mock.MagicMock()
resp = dfa_client.get_agents_details()
mock_client.return_value.call.assert_called_with(
{}, 'get_agents_details', msg={})
self.assertEqual(resp, mock_client.return_value.call.return_value)
def test_get_agent_details_per_host(self):
agent = dict(tenant_id=1)
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as \
mock_client:
mock_client.return_value.call = mock.MagicMock()
resp = dfa_client.get_agent_details_per_host(agent)
mock_client.return_value.call.assert_called_with(
{}, 'get_agent_details_per_host', msg=agent)
self.assertEqual(resp, mock_client.return_value.call.return_value)

View File

@ -0,0 +1,132 @@
# 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.core.urlresolvers import reverse
from horizon_cisco_ui.cisco.dfa.tabs import dfa_client as dc
from horizon_cisco_ui.cisco.dfa.test import test_data
import mock
from openstack_dashboard.test import helpers as test
class DFATestCase(test.BaseAdminViewTests):
def setUp(self):
super(DFATestCase, self).setUp()
self.dfa_client = dc.DFAClient
def _setup_test_data(self):
super(DFATestCase, self)._setup_test_data()
test_data.data(self)
def _test_base_index(self):
profiles = self.dfa_config_profile.list()
projects = self.dfa_project.list()
networks = self.dfa_network.list()
instances = self.dfa_instance.list()
agents = self.dfa_agent.list()
summary = self.dfa_summary.list()[0]
with mock.patch('horizon_cisco_ui.cisco.dfa.tabs.dfa_client'
'.get_config_profiles_detail', return_value=profiles), \
mock.patch('horizon_cisco_ui.cisco.dfa.tabs.dfa_client'
'.get_network_by_tenant_id',
return_value=networks), \
mock.patch('horizon_cisco_ui.cisco.dfa.tabs.dfa_client'
'.get_instance_by_tenant_id',
return_value=instances), \
mock.patch('horizon_cisco_ui.cisco.dfa.tabs.dfa_client'
'.get_project_details',
return_value=projects), \
mock.patch('horizon_cisco_ui.cisco.dfa.tabs.dfa_client'
'.get_fabric_summary',
return_value=summary), \
mock.patch('horizon_cisco_ui.cisco.dfa.tabs.dfa_client'
'.get_agents_details',
return_value=agents):
res = self.client.get(reverse('horizon:cisco:dfa:index'))
self.assertTemplateUsed(res, 'cisco/dfa/index.html')
return res
def test_fabric_summary_tab_index(self):
res = self._test_base_index()
services_tab = res.context['tab_group'].get_tab('fabric_summary_tab')
self.assertEqual(
services_tab._tables['fabricsummary'].data,
[summary for summary in self.dfa_summary.list()[0]])
def test_cfg_profile_index(self):
res = self._test_base_index()
services_tab = res.context['tab_group'].get_tab('cfgprofile_tab')
self.assertEqual(
services_tab._tables['cfgprofile'].data,
[profiles for profiles in self.dfa_config_profile.list()])
def test_nfe_info_index(self):
res = self._test_base_index()
services_tab = res.context['tab_group'].get_tab('nfe_info_tab')
self.assertEqual(
services_tab._tables['projecttable'].data,
[project for project in self.dfa_project.list()])
self.assertEqual(
services_tab._tables['networktable'].data,
[network for network in self.dfa_network.list()])
def test_nfe_agents_index(self):
res = self._test_base_index()
services_tab = res.context['tab_group'].get_tab('nfe_agents_tab')
self.assertEqual(
services_tab._tables['agentstable'].data,
[agent for agent in self.dfa_agent.list()])
def test_agent_detail(self):
agent = self.dfa_agent.list()
with mock.patch('horizon_cisco_ui.cisco.dfa.tabs.dfa_client'
'.get_agent_details_per_host',
return_value=agent):
res = self.client.get(reverse('horizon:cisco:dfa:detail',
args=['compute0']))
self.assertTemplateUsed(res, 'cisco/dfa/detail.html')
def test_agent_detail_exception(self):
with mock.patch('horizon_cisco_ui.cisco.dfa.tabs.dfa_client'
'.get_agent_details_per_host',
side_effect=dc.exceptions.NotAvailable):
url = reverse('horizon:cisco:dfa:detail', args=['compute0'])
res = self.client.get(url)
redir_url = reverse('horizon:cisco:dfa:index')
self.assertRedirectsNoFollow(res, redir_url)
def test_associate_dci_to_project(self):
formdata = {'project_id': 123456, 'dci_id': 1001}
with mock.patch('horizon_cisco_ui.cisco.dfa.tabs.dfa_client'
'.do_associate_dci_id_to_project'):
url = reverse('horizon:cisco:dfa:associate', args=['123456'])
res = self.client.post(url, formdata)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, '/cisco/dfa/')
def test_disassociate_dci_to_project(self):
formdata = {'project_id': 123456, 'dci_id': 0}
with mock.patch('horizon_cisco_ui.cisco.dfa.tabs.dfa_client'
'.do_associate_dci_id_to_project'):
url = reverse('horizon:cisco:dfa:disassociate', args=['123456'])
res = self.client.post(url, formdata)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, '/cisco/dfa/')

View File

@ -0,0 +1,98 @@
# Copyright 2012 Nebula, Inc.
#
# 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 openstack_dashboard.test.test_data import utils
def data(TEST):
# Test DataContainers for DFA Workflow
TEST.dfa_precreate_network = utils.TestDataContainer()
TEST.dfa_config_profile = utils.TestDataContainer()
TEST.dfa_project = utils.TestDataContainer()
TEST.dfa_network = utils.TestDataContainer()
TEST.dfa_instance = utils.TestDataContainer()
TEST.dfa_agent = utils.TestDataContainer()
TEST.dfa_summary = utils.TestDataContainer()
precreate_dict = {'tenant_id': '1',
'nwk_name': 'net1',
'subnet': '10.0.0.0/24',
'cfgp': 'defaultNetworkL2Profile'}
TEST.dfa_precreate_network.add(precreate_dict)
cfg_profile_dict = {'profileSubType': 'network:universal',
'description': 'native dhcp EF Profile',
'editable': 'yes',
'configCommands': 'vlan $vlanId',
'profileType': 'IPBD',
'profileName': 'nativeDhcpEfProfile',
'forwardingMode': 'proxy-gateway',
'modifyTimestamp': 'Wed Mar 30 212631 PDT 2016'}
TEST.dfa_config_profile.add(cfg_profile_dict)
project_dict = {'project_id': 'cc073fa35b544e27b6a7802e9919afb1',
'dci_id': 12344,
'project_name': 'Cisco',
'result': 'SUCCESS'}
TEST.dfa_project.add(project_dict)
network_dict = {'network_id': '05df940e-7a68-4ead-9618-ae199c4dc289',
'reason': 'Request to DCNM failed: [500] Segment ID: \
76388 already exists.',
'seg_id': 76388,
'result': 'CREATE_FAIL',
'config_profile': 'defaultNetworkL2Profile',
'network_name': 'net2',
'vlan_id': None}
TEST.dfa_network.add(network_dict)
network_dict = {'network_id': '7c67bc14-8b7b-44ea-9ed5-c3fb36d7bfd9',
'reason': None,
'seg_id': 76377,
'result': 'SUCCESS',
'config_profile': 'defaultNetworkUniversalEfProfile',
'network_name': 'network1',
'vlan_id': 10}
TEST.dfa_network.add(network_dict)
instance_dict = {'local_vlan': 10,
'name': 'INS1',
'network_id': '7c67bc14-8b7b-44ea-9ed5-c3fb36d7bfd9',
'instance_id': 'e7cd03bed37b4de48f19fc5232056025',
'host': 'ctayal-openstack',
'veth_intf': 'eth1',
'remote_system_name': 'N6k-75',
'remote_port': 'Ethernet1/47',
'seg_id': 76377,
'result': 'SUCCESS',
'vdp_vlan': 110,
'port_id': '6672d670-9ba9-4889-8754-367bd51165fd'}
TEST.dfa_instance.add(instance_dict)
agent_dict = {'heartbeat': '2016-09-01T04:40:17.000000',
'host': 'compute0',
'config': '{"memb_ports": null, "veth_intf": "", \
"uplink": ""}',
'created': u'2016-07-27T06:34:48.000000'}
TEST.dfa_agent.add(agent_dict)
summary_dict = [{u'value': 2.0, u'key': u'Fabric Enabler Version'},
{u'value': u'10.0(1)', u'key': u'DCNM Version'},
{u'value': u'172.28.11.151', u'key': u'DCNM IP'},
{u'value': u'n6k', u'key': u'Switch Type'},
{u'value': u'fabricpath', u'key': u'Fabric Type'},
{u'value': u'2', u'key': u'Fabric ID'},
{u'value': u'76345-76545', u'key': u'Segment ID Range'}]
TEST.dfa_summary.add(summary_dict)

View File

@ -0,0 +1,33 @@
# 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.conf.urls import patterns
from django.conf.urls import url
from horizon_cisco_ui.cisco.dfa import views
AGENTS = r'^(?P<host>[^/]+)/%s$'
PROJECT = r'^(?P<project_id>[^/]+)/%s$'
PROFILE = r'^(?P<profile_name>[^/]+)/%s$'
urlpatterns = patterns(
'',
url(r'^$', views.IndexView.as_view(), name='index'),
url(AGENTS % 'detail', views.DetailView.as_view(), name='detail'),
url(PROFILE % 'detailprofile', views.DetailProfileView.as_view(),
name='detailprofile'),
url(PROJECT % 'associate', views.AssociateDCIToProjectView.as_view(),
name='associate'),
url(PROJECT % 'disassociate', views.DisssociateDCIToProjectView.as_view(),
name='disassociate'),
)

View File

@ -0,0 +1,191 @@
# 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.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django.utils.translation import ugettext_lazy as _
import json
import logging
import re
from horizon import exceptions
from horizon import forms
from horizon import tables
from horizon import tabs
from horizon.utils import memoized
from horizon import views
from horizon_cisco_ui.cisco.dfa import dfa_client
from horizon_cisco_ui.cisco.dfa import forms as dfaforms
from horizon_cisco_ui.cisco.dfa import tables as dfatables
from horizon_cisco_ui.cisco.dfa import tabs as dfatab
LOG = logging.getLogger(__name__)
class IndexView(tabs.TabbedTableView):
tab_group_class = dfatab.DFATabs
template_name = 'cisco/dfa/index.html'
page_title = _("Programmable Fabric")
class AssociateDCIToProjectView(forms.ModalFormView):
form_class = dfaforms.AssociateDCI
form_id = "associate_form"
modal_header = _("Associate DCI ID to Project")
template_name = 'cisco/dfa/associate.html'
submit_label = _("ASSOCIATE")
submit_url = "horizon:cisco:dfa:associate"
success_url = reverse_lazy('horizon:cisco:dfa:index')
page_title = _("ASSOCIATE DCI ID")
def get_context_data(self, **kwargs):
context = super(AssociateDCIToProjectView,
self).get_context_data(**kwargs)
args = (self.kwargs['project_id'],)
context["project_id"] = self.kwargs['project_id']
context["submit_url"] = reverse(self.submit_url, args=args)
return context
@memoized.memoized_method
def _get_object(self, *args, **kwargs):
return self.kwargs["project_id"]
def get_initial(self):
return {'project_id': self._get_object()}
class DisssociateDCIToProjectView(forms.ModalFormView):
form_class = dfaforms.DisassociateDCI
form_id = "disassociate_form"
modal_header = _("Disassociate DCI ID to Project")
template_name = 'cisco/dfa/disassociate.html'
submit_label = _("DISASSOCIATE")
submit_url = "horizon:cisco:dfa:disassociate"
success_url = reverse_lazy('horizon:cisco:dfa:index')
def get_context_data(self, **kwargs):
context = super(DisssociateDCIToProjectView,
self).get_context_data(**kwargs)
args = (self.kwargs['project_id'],)
context["project_id"] = self.kwargs['project_id']
context["submit_url"] = reverse(self.submit_url, args=args)
return context
@memoized.memoized_method
def _get_object(self, *args, **kwargs):
return self.kwargs["project_id"]
def get_initial(self):
return {'project_id': self._get_object()}
class DetailProfileView(views.HorizonTemplateView):
template_name = 'cisco/dfa/detailprofile.html'
page_title = "{{ profile_name }}"
def get_context_data(self, **kwargs):
context = super(DetailProfileView, self).get_context_data(**kwargs)
data = self._get_data()
rep = {'\r': '<br>', ' ': '&nbsp;'}
rep = dict((re.escape(k), v) for k, v in rep.iteritems())
pattern = re.compile("|".join(rep.keys()))
commands = pattern.sub(lambda m: rep[re.escape(m.group(0))],
data.get('configCommands'))
context['Profile_Name'] = self.kwargs['profile_name'].split(":")[0]
context['Profile_Type'] = self.kwargs['profile_name'].split(":")[1]
context['fwding_mode'] = data.get('forwardingMode')
context['description'] = data.get('description')
context['commands'] = commands
return context
@memoized.memoized_method
def _get_data(self):
try:
profile_name = self.kwargs['profile_name']
cfg_profile = {'profile': profile_name.split(":")[0],
'ftype': profile_name.split(":")[1]}
data = dfa_client.get_per_config_profile_detail(cfg_profile)
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve host details.'),
redirect=self.get_redirect_url())
return data
@staticmethod
def get_redirect_url():
return reverse_lazy('horizon:cisco:dfa:index')
class DetailView(tables.MultiTableView):
table_classes = (dfatables.TopologyTable, )
template_name = 'cisco/dfa/detail.html'
page_title = "{{ agent.host }}"
def get_topology_data(self):
try:
topology = []
agent = self._get_data()
cfg = json.loads(agent.get('config'))
topo = cfg.get('topo')
for key in topo.keys():
intf = topo.get(key)
if not intf.get('remote_evb_cfgd'):
continue
topology.append(
dict(
interface=cfg.get('uplink'),
remote_port=intf.get('remote_port'),
bond_intf=intf.get('bond_intf'),
remote_port_mac=intf.get('remote_port_id_mac'),
remote_evb_cfgd=intf.get('remote_evb_cfgd'),
remote_system_desc=intf.get('remote_system_desc'),
remote_chassis_mac=intf.get('remote_chassis_id_mac'),
remote_mgmt_addr=intf.get('remote_mgmt_addr'),
remote_system_name=intf.get('remote_system_name'),
remote_evb_mode=intf.get('remote_evb_mode')))
except Exception:
topology = []
msg = _('Neighborship is not established for this server')
exceptions.handle(self.request, msg)
return topology
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
agent = self._get_data()
context["agent"] = agent
cfg = json.loads(agent.get('config'))
context["uplink"] = cfg.get('uplink')
context["memb_ports"] = cfg.get('memb_ports')
context["veth_intf"] = cfg.get('veth_intf')
context["url"] = self.get_redirect_url()
return context
@memoized.memoized_method
def _get_data(self):
try:
host_name = self.kwargs['host']
NFEhost = dict(host=host_name)
agent = (dfa_client.get_agent_details_per_host(NFEhost))[0]
agent["heartbeat"] = agent.get('heartbeat').replace('T', ' ')[:-7]
agent["created"] = agent.get('created').replace('T', ' ')[:-7]
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve host details.'),
redirect=self.get_redirect_url())
return agent
@staticmethod
def get_redirect_url():
return reverse_lazy('horizon:cisco:dfa:index')

View File

@ -43,10 +43,7 @@ class DFAConfigProfileAction(workflows.Action):
def _get_cfg_profiles(self, request):
profiles = []
try:
dfaclient = dfa_client.DFAClient()
if not bool(dfaclient.__dict__):
return profiles
cfgplist = dfaclient.get_config_profiles_detail()
cfgplist = dfa_client.get_config_profiles_detail()
profiles = [q for p in cfgplist
if (p.get('profileSubType') == 'network:universal')
for q in [p.get('profileName')]]
@ -89,12 +86,11 @@ class DFACreateNetwork(upstream_networks_workflows.CreateNetwork):
if not network:
return False
if data['cfg_profile']:
dfaclient = dfa_client.DFAClient()
associate_data = {'id': network['id'],
'cfgp': data['cfg_profile'],
'name': network['name'],
'tenant_id': request.user.project_id}
dfaclient.associate_profile_with_network(associate_data)
dfa_client.associate_profile_with_network(associate_data)
# If we do not need to create a subnet, return here.
if not data['with_subnet']:
return True

View File

@ -0,0 +1,5 @@
PANEL = 'dfa'
PANEL_GROUP = 'default'
PANEL_DASHBOARD = 'cisco'
ADD_PANEL = 'horizon_cisco_ui.cisco.dfa.panel.DFA'