Programmable Fabric Panel in Cisco dashboard
It displays Nexus Programmable Fabric information for troubleshooting and provide fabric summary, topology, network profile information Change-Id: I84e1d2b1b163104be5b84411056141aa3ada6f95
This commit is contained in:
parent
b6073359ff
commit
1c270af393
1
AUTHORS
1
AUTHORS
@ -1,2 +1,3 @@
|
|||||||
Rob Cresswell <robert.cresswell@outlook.com>
|
Rob Cresswell <robert.cresswell@outlook.com>
|
||||||
Saksham Varma <sakvarma@cisco.com>
|
Saksham Varma <sakvarma@cisco.com>
|
||||||
|
Chirag Tayal <chiragtayal@gmail.com>
|
||||||
|
@ -14,12 +14,10 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import ConfigParser
|
import ConfigParser
|
||||||
import json
|
|
||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
from horizon import exceptions
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from horizon.utils.memoized import memoized
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import oslo_messaging as messaging
|
import oslo_messaging as messaging
|
||||||
@ -30,11 +28,14 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from horizon import exceptions
|
||||||
|
from horizon.utils.memoized import memoized
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class DFAClient(object):
|
class DFAClient(object):
|
||||||
"""Represents fabric enabler command line interface."""
|
"""Represents Nexus Fabric Enabler RPC Client."""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.setup_client()
|
self.setup_client()
|
||||||
@ -58,30 +59,144 @@ class DFAClient(object):
|
|||||||
|
|
||||||
return self.clnt
|
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):
|
@memoized
|
||||||
'''Get all config Profiles details from the Fabric Enabler'''
|
def dfaclient():
|
||||||
|
return DFAClient().clnt
|
||||||
|
|
||||||
context = {}
|
|
||||||
args = json.dumps({})
|
def associate_profile_with_network(network):
|
||||||
try:
|
'''Associate Network Profile with network'''
|
||||||
resp = self.clnt.call(context, 'get_config_profiles_detail',
|
|
||||||
msg=args)
|
try:
|
||||||
return resp
|
resp = dfaclient().call({}, 'associate_profile_with_network',
|
||||||
except (messaging.MessagingException, messaging.RemoteError,
|
msg=network)
|
||||||
messaging.MessagingTimeout):
|
return resp
|
||||||
LOG.error("RPC: Request for detailed Config Profiles failed.")
|
except (messaging.MessagingException, messaging.RemoteError,
|
||||||
raise exceptions.NotAvailable("RPC to Fabric Enabler failed")
|
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")
|
||||||
|
63
horizon_cisco_ui/cisco/dfa/forms.py
Normal file
63
horizon_cisco_ui/cisco/dfa/forms.py
Normal 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
|
43
horizon_cisco_ui/cisco/dfa/panel.py
Normal file
43
horizon_cisco_ui/cisco/dfa/panel.py
Normal 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)
|
234
horizon_cisco_ui/cisco/dfa/tables.py
Normal file
234
horizon_cisco_ui/cisco/dfa/tables.py
Normal 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
|
133
horizon_cisco_ui/cisco/dfa/tabs.py
Normal file
133
horizon_cisco_ui/cisco/dfa/tabs.py
Normal 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
|
7
horizon_cisco_ui/cisco/dfa/templates/dfa/_associate.html
Normal file
7
horizon_cisco_ui/cisco/dfa/templates/dfa/_associate.html
Normal 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 %}
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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 %}
|
19
horizon_cisco_ui/cisco/dfa/templates/dfa/_instance.html
Normal file
19
horizon_cisco_ui/cisco/dfa/templates/dfa/_instance.html
Normal 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>
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
<a href="#" data-toggle="tooltip" data-placement="right" data-trigger="focus" data-original-title="
|
||||||
|
{{ description }}
|
||||||
|
">{{ name }}</a>
|
14
horizon_cisco_ui/cisco/dfa/templates/dfa/_reason.html
Normal file
14
horizon_cisco_ui/cisco/dfa/templates/dfa/_reason.html
Normal 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>
|
7
horizon_cisco_ui/cisco/dfa/templates/dfa/associate.html
Normal file
7
horizon_cisco_ui/cisco/dfa/templates/dfa/associate.html
Normal 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 %}
|
16
horizon_cisco_ui/cisco/dfa/templates/dfa/detail.html
Normal file
16
horizon_cisco_ui/cisco/dfa/templates/dfa/detail.html
Normal 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 %}
|
||||||
|
|
12
horizon_cisco_ui/cisco/dfa/templates/dfa/detailprofile.html
Normal file
12
horizon_cisco_ui/cisco/dfa/templates/dfa/detailprofile.html
Normal 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 %}
|
||||||
|
|
@ -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 %}
|
@ -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 %}
|
12
horizon_cisco_ui/cisco/dfa/templates/dfa/index.html
Normal file
12
horizon_cisco_ui/cisco/dfa/templates/dfa/index.html
Normal 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 %}
|
@ -1,3 +1,6 @@
|
|||||||
|
# Copyright 2014 Cisco Systems, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# not use this file except in compliance with the License. You may obtain
|
||||||
# a copy of the License at
|
# a copy of the License at
|
||||||
@ -10,7 +13,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import json
|
|
||||||
import mock
|
import mock
|
||||||
import platform
|
import platform
|
||||||
|
|
||||||
@ -38,12 +40,13 @@ class DFAClientTestCase(test.BaseAdminViewTests):
|
|||||||
name='net1',
|
name='net1',
|
||||||
tenant_id=1)
|
tenant_id=1)
|
||||||
|
|
||||||
message = json.dumps(network)
|
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as \
|
||||||
with mock.patch.object(self.client.clnt, 'call') as mock_call:
|
mock_client:
|
||||||
self.client.associate_profile_with_network(network)
|
mock_client.return_value.call = mock.MagicMock()
|
||||||
|
dfa_client.associate_profile_with_network(network)
|
||||||
|
|
||||||
mock_call.assert_called_with({}, 'associate_profile_with_network',
|
mock_client.return_value.call.assert_called_with(
|
||||||
msg=message)
|
{}, 'associate_profile_with_network', msg=network)
|
||||||
|
|
||||||
def test_associate_profile_with_network_rpc_exception(self):
|
def test_associate_profile_with_network_rpc_exception(self):
|
||||||
network = dict(id='1125-as45-afg5f-3457',
|
network = dict(id='1125-as45-afg5f-3457',
|
||||||
@ -51,10 +54,97 @@ class DFAClientTestCase(test.BaseAdminViewTests):
|
|||||||
name='net1',
|
name='net1',
|
||||||
tenant_id=1)
|
tenant_id=1)
|
||||||
|
|
||||||
with mock.patch.object(self.client.clnt, 'call',
|
with mock.patch('horizon_cisco_ui.cisco.dfa.dfa_client.dfaclient') as mock_client, \
|
||||||
side_effect=dfa_client.messaging.MessagingException), \
|
|
||||||
self.assertRaises(dfa_client.exceptions.NotAvailable) as cm:
|
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',
|
self.assertEqual('RPC to Fabric Enabler failed',
|
||||||
str(cm.exception))
|
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)
|
||||||
|
132
horizon_cisco_ui/cisco/dfa/test/dfa_horizon_test.py
Normal file
132
horizon_cisco_ui/cisco/dfa/test/dfa_horizon_test.py
Normal 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/')
|
98
horizon_cisco_ui/cisco/dfa/test/test_data.py
Normal file
98
horizon_cisco_ui/cisco/dfa/test/test_data.py
Normal 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)
|
33
horizon_cisco_ui/cisco/dfa/urls.py
Normal file
33
horizon_cisco_ui/cisco/dfa/urls.py
Normal 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'),
|
||||||
|
)
|
191
horizon_cisco_ui/cisco/dfa/views.py
Normal file
191
horizon_cisco_ui/cisco/dfa/views.py
Normal 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>', ' ': ' '}
|
||||||
|
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')
|
@ -43,10 +43,7 @@ class DFAConfigProfileAction(workflows.Action):
|
|||||||
def _get_cfg_profiles(self, request):
|
def _get_cfg_profiles(self, request):
|
||||||
profiles = []
|
profiles = []
|
||||||
try:
|
try:
|
||||||
dfaclient = dfa_client.DFAClient()
|
cfgplist = dfa_client.get_config_profiles_detail()
|
||||||
if not bool(dfaclient.__dict__):
|
|
||||||
return profiles
|
|
||||||
cfgplist = dfaclient.get_config_profiles_detail()
|
|
||||||
profiles = [q for p in cfgplist
|
profiles = [q for p in cfgplist
|
||||||
if (p.get('profileSubType') == 'network:universal')
|
if (p.get('profileSubType') == 'network:universal')
|
||||||
for q in [p.get('profileName')]]
|
for q in [p.get('profileName')]]
|
||||||
@ -89,12 +86,11 @@ class DFACreateNetwork(upstream_networks_workflows.CreateNetwork):
|
|||||||
if not network:
|
if not network:
|
||||||
return False
|
return False
|
||||||
if data['cfg_profile']:
|
if data['cfg_profile']:
|
||||||
dfaclient = dfa_client.DFAClient()
|
|
||||||
associate_data = {'id': network['id'],
|
associate_data = {'id': network['id'],
|
||||||
'cfgp': data['cfg_profile'],
|
'cfgp': data['cfg_profile'],
|
||||||
'name': network['name'],
|
'name': network['name'],
|
||||||
'tenant_id': request.user.project_id}
|
'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 we do not need to create a subnet, return here.
|
||||||
if not data['with_subnet']:
|
if not data['with_subnet']:
|
||||||
return True
|
return True
|
||||||
|
5
horizon_cisco_ui/enabled/_6020_dfa.py
Normal file
5
horizon_cisco_ui/enabled/_6020_dfa.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
PANEL = 'dfa'
|
||||||
|
PANEL_GROUP = 'default'
|
||||||
|
PANEL_DASHBOARD = 'cisco'
|
||||||
|
|
||||||
|
ADD_PANEL = 'horizon_cisco_ui.cisco.dfa.panel.DFA'
|
Loading…
Reference in New Issue
Block a user