Create DataNetworks modelling in System Configuration

Update stx-gui for DataNetwork modelling of the
This is part of the Story to "Move neutron provider network modelling
to system configuration".

The following GUI panels are updated:
    Data Networks
        - Add creation of vxlan datanetwork
    Data Network Topology
        - Update datanetwork-host-lldp topology
        - Remove segment info from Data (Provider) Network

Change-Id: I9bcd9408fd1f4ca7cd905397b09005af1897684b
Story: 2004455
Task: 28325
Signed-off-by: John Kung <john.kung@windriver.com>
This commit is contained in:
John Kung 2019-01-31 16:57:54 -05:00
parent fdc3ed8857
commit 282306012d
16 changed files with 184 additions and 133 deletions

View File

@ -1,2 +1,2 @@
SRC_DIR="starlingx-dashboard"
TIS_PATCH_VER=25
TIS_PATCH_VER=26

View File

@ -39,8 +39,6 @@ class CreateDataNetwork(forms.SelfHandlingForm):
description = forms.CharField(max_length=255,
label=_("Description"),
required=False)
network_type = forms.ChoiceField(label=_("Type"),
required=True)
mtu = forms.IntegerField(
label=_("MTU"),
required=True,
@ -55,6 +53,70 @@ class CreateDataNetwork(forms.SelfHandlingForm):
"1500 requires a minimum data interface MTU of 1574 bytes (1600 "
"bytes is recommended.")))
network_type = forms.ChoiceField(label=_("Type"),
required=True,
widget=forms.Select(
attrs={
'class': 'switchable',
'data-slug': 'dn_type'}))
# VXLAN specific fields
mode_choices = [('dynamic', _('Multicast VXLAN')),
('static', _('Static VXLAN'))]
mode = forms.ChoiceField(label=_("Mode"),
initial='dynamic',
required=False,
choices=mode_choices,
widget=forms.Select(
attrs={
'class': 'switchable switched',
'data-switch-on': 'dn_type',
'data-dn_type-vxlan': 'Mode',
'data-slug': 'vxlan_mode'}))
multicast_group_help = (_("Specify the IPv4 or IPv6 multicast address "
"for these VXLAN instances"))
multicast_group = forms.CharField(
max_length=255,
label=_("Multicast Group Address"),
initial="239.0.0.1",
required=False,
help_text=multicast_group_help,
widget=forms.TextInput(
attrs={
'class': 'switchable switched',
'data-slug': 'vxlan_multicast_group',
'data-switch-on': 'vxlan_mode',
'data-vxlan_mode-dynamic': 'Multicast Group Address'})
)
port_num_choices = [('4789', _('IANA Assigned VXLAN UDP port (4789)')),
('4790', _('IANA Assigned VXLAN-GPE UDP port (4790)')),
('8472', _('Legacy VXLAN UDP port (8472)'))]
port_num = forms.ChoiceField(label=_("UDP Port"),
required=True,
choices=port_num_choices,
widget=forms.Select(
attrs={
'class': 'switchable switched',
'data-switch-on': 'dn_type',
'data-dn_type-vxlan': 'UDP Port',
'data-slug': 'port_num_slug'}))
ttl = forms.IntegerField(label=_("TTL"),
required=False,
initial=1,
min_value=1,
max_value=255,
widget=forms.TextInput(
attrs={
'class': 'switchable switched',
'data-switch-on': 'dn_type',
'data-dn_type-vxlan': 'TTL',
'data-slug': 'ttl_slug'}),
help_text=(
_("Specify the time-to-live value for these VXLAN instances")))
@classmethod
def _instantiate(cls, request, *args, **kwargs):
return cls(request, *args, **kwargs)
@ -83,6 +145,12 @@ class CreateDataNetwork(forms.SelfHandlingForm):
'description': data['description'],
'mtu': data['mtu']}
if data['network_type'] == stx_api.sysinv.DATANETWORK_TYPE_VXLAN:
params.update({'mode': data['mode'],
'port_num': data['port_num'],
'ttl': data['ttl']})
if data['mode'] == 'dynamic':
params.update({'multicast_group': data['multicast_group']})
network = stx_api.sysinv.data_network_create(request,
**params)
msg = (_('Data network %s was successfully created.') %
@ -128,13 +196,13 @@ class UpdateDataNetwork(forms.SelfHandlingForm):
params = {'description': data['description'],
'mtu': data['mtu']}
providernet = stx_api.sysinv.data_network_modify(
datanet = stx_api.sysinv.data_network_modify(
request, data['id'], **params)
msg = (_('Data network %s was successfully updated.') %
data['name'])
LOG.info(msg)
messages.success(request, msg)
return providernet
return datanet
except sysinv_exceptions.CgtsclientException as e:
msg = _('Failed to update data network %s') % data['name']
LOG.info(msg)

View File

@ -83,19 +83,18 @@ class AddDataNetworkRange(tables.LinkAction):
url = "horizon:admin:datanets:datanets:addrange"
classes = ("ajax-modal", "btn-edit")
def allowed(self, request, providernet):
if providernet:
return providernet.network_type not in ('flat')
def allowed(self, request, datanet):
if datanet:
return datanet.network_type not in ('flat')
return super(AddDataNetworkRange, self).allowed(request,
providernet)
datanet)
class DataNetworksFilterAction(tables.FilterAction):
def filter(self, table, datanets, filter_string):
"""Naive case-insensitive search."""
q = filter_string.lower()
return [providernet for providernet in datanets
if q in providernet.name.lower()]
return [dn for dn in datanets if q in dn.name.lower()]
def _format_providernet_ranges(data):
@ -115,7 +114,7 @@ class DataNetworksTable(tables.DataTable):
return str(datum.uuid)
class Meta(object):
name = "provider_networks"
name = "data_networks"
verbose_name = _("Data Networks")
table_actions = (CreateDataNetwork, DeleteDataNetwork,
DataNetworksFilterAction)

View File

@ -53,7 +53,7 @@ class DetailView(tables.MultiTableView):
providernet_tables.ProviderNetworkTenantNetworkTable)
template_name = 'admin/datanets/datanets/detail.html'
failure_url = reverse_lazy('horizon:admin:datanets:index')
page_title = '{{ "Data Network Detail: "|add:providernet.name }}'
page_title = '{{ "Data Network Detail: "|add:datanet.name }}'
def _get_tenant_list(self):
if not hasattr(self, "_tenants"):
@ -116,7 +116,7 @@ class DetailView(tables.MultiTableView):
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context["providernet"] = self._get_data()
context["datanet"] = self._get_data()
context["nova_providernet"] = self._get_nova_data()
return context
@ -144,12 +144,12 @@ class UpdateView(forms.ModalFormView):
return self._object
def get_initial(self):
providernet = self._get_object()
return {'id': providernet.id,
'name': providernet.name,
'network_type': providernet.network_type,
'mtu': providernet.mtu,
'description': providernet.description,
datanet = self._get_object()
return {'id': datanet.id,
'name': datanet.name,
'network_type': datanet.network_type,
'mtu': datanet.mtu,
'description': datanet.description,
}

View File

@ -18,10 +18,10 @@ from starlingx_dashboard.dashboards.admin.datanets.datanets import \
class DataNetworkTab(tabs.TableTab):
table_classes = (datanets_tables.DataNetworksTable,)
name = _("Data Networks")
slug = "provider_networks"
slug = "data_networks"
template_name = ("horizon/common/_detail_table.html")
def get_provider_networks_data(self):
def get_data_networks_data(self):
try:
datanets = \
stx_sysinv.data_network_list(self.tab_group.request)

View File

@ -5,15 +5,27 @@
<div class="info detail">
<dl class="dl-horizontal">
<dt>{% trans "Name" %}</dt>
<dd>{{ providernet.name|default:_("None") }}</dd>
<dd>{{ datanet.name|default:_("None") }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ providernet.id|default:_("None") }}</dd>
<dd>{{ datanet.id|default:_("None") }}</dd>
<dt>{% trans "Type" %}</dt>
<dd>{{ providernet.type|default:_("None") }}</dd>
<dd>{{ datanet.network_type|default:_("None") }}</dd>
<dt>{% trans "MTU" %}</dt>
<dd>{{ providernet.mtu|default:_("-") }}</dd>
<dd>{{ datanet.mtu|default:_("-") }}</dd>
<dt>{% trans "Description" %}</dt>
<dd>{{ providernet.description|default:_("None") }}</dd>
<dd>{{ datanet.description|default:_("None") }}</dd>
{% if datanet.network_type == 'vxlan' %}
<dt>{% trans "Mode" %}</dt>
<dd>{{ datanet.mode }}</dd>
<dt>{% trans "UDP Port Number" %}</dt>
<dd>{{ datanet.port_num }}</dd>
<dt>{% trans "Time to Live" %}</dt>
<dd>{{ datanet.ttl }}</dd>
{% endif %}
{% if datanet.mode == 'dynamic' %}
<dt>{% trans "Multicast Group Address" %}</dt>
<dd>{{ datanet.multicast_group|default:_("-") }}</dd>
{% endif %}
{% if nova_providernet %}
<dt>{% trans "PCI PFs Configured" %}</dt>
<dd>{{ nova_providernet.pci_pfs_configured|default:_("0") }}</dd>

View File

@ -5,10 +5,4 @@
{% block main %}
{% include "admin/datanets/datanets/_detail_overview.html" %}
<hr>
<div id="ranges">
{{ provider_network_ranges_table.render }}
</div>
<div id="tenant_networks">
{{ tenant_networks_table.render }}
</div>
{% endblock %}

View File

@ -8,7 +8,7 @@ from django.conf.urls import include
from django.conf.urls import url
from starlingx_dashboard.dashboards.admin.datanets.datanets \
import urls as providernet_urls
import urls as datanet_urls
from starlingx_dashboard.dashboards.admin.datanets import views
@ -17,5 +17,5 @@ NETWORKS = r'^(?P<network_id>[^/]+)/%s$'
urlpatterns = [
url(r'^$', views.IndexViewTabbed.as_view(), name='index'),
url(r'^datanets/',
include(providernet_urls, namespace='datanets'))
include(datanet_urls, namespace='datanets'))
]

View File

@ -14,7 +14,6 @@ from horizon import exceptions
from horizon import tabs
from openstack_dashboard import api as api
from starlingx_dashboard import api as stx_api
from starlingx_dashboard.dashboards.admin.datanets.datanets import \
tables as pn_tables
from starlingx_dashboard.dashboards.admin.host_topology import \
@ -47,7 +46,7 @@ class AlarmsTab(tabs.TableTab):
def get_alarms_data(self):
entity = self.tab_group.kwargs.get('host')
if not entity:
entity = self.tab_group.kwargs.get('providernet')
entity = self.tab_group.kwargs.get('datanet')
return entity.alarms
@ -65,9 +64,9 @@ class HostDetailTabs(tabs.TabGroup):
class OverviewTab(tabs.TableTab):
table_classes = (tables.ProviderNetworkRangeTable,
pn_tables.ProviderNetworkTenantNetworkTable)
template_name = 'admin/host_topology/detail/providernet.html'
template_name = 'admin/host_topology/detail/datanet.html'
name = "Data Network Detail"
slug = 'providernet_details_overview'
slug = 'datanet_details_overview'
failure_url = reverse_lazy('horizon:admin:host_topology:index')
def _get_tenant_list(self):
@ -88,31 +87,18 @@ class OverviewTab(tabs.TableTab):
return networks
def get_provider_network_ranges_data(self):
try:
providernet_id = self.tab_group.kwargs['providernet_id']
ranges = stx_api.neutron.provider_network_range_list(
self.request, providernet_id=providernet_id)
except Exception:
ranges = []
msg = _('Segmentation id range list can not be retrieved.')
exceptions.handle(self.request, msg)
tenant_dict = self._get_tenant_list()
for r in ranges:
r.set_id_as_name_if_empty()
# Set tenant name
tenant = tenant_dict.get(r.tenant_id, None)
r.tenant_name = getattr(tenant, 'name', None)
ranges = []
return ranges
def get_context_data(self, request):
context = super(OverviewTab, self).get_context_data(request)
try:
context['providernet'] = self.tab_group.kwargs['providernet']
context['nova_providernet'] = \
self.tab_group.kwargs['nova_providernet']
context['datanet'] = self.tab_group.kwargs['datanet']
context['nova_datanet'] = \
self.tab_group.kwargs['nova_datanet']
except Exception:
exceptions.handle(self.request,
_('Unable to retrieve providernet details.'))
_('Unable to retrieve datanet details.'))
return context

View File

@ -0,0 +1,7 @@
{% load i18n sizeformat %}
<div class="info row-fluid detail">
{% include "admin/datanets/datanets/_detail_overview.html" %}
<hr>
</div>

View File

@ -1,13 +0,0 @@
{% load i18n sizeformat %}
<div class="info row-fluid detail">
{% include "admin/datanets/datanets/_detail_overview.html" %}
<hr>
<div id="ranges">
{{ provider_network_ranges_table.render }}
</div>
<div id="tenant_networks">
{{ tenant_networks_table.render }}
</div>
</div>

View File

@ -4,7 +4,7 @@
{% if host %}
<h4>Selected Entity: <a href="{% url 'horizon:admin:inventory:detail' host.id %}" >{{host.hostname}}</a></h4>
{% else %}
<h4>Selected Entity: <a href="{% url 'horizon:admin:datanets:datanets:detail' providernet.id %}" >{{providernet.name}}</a></h4>
<h4>Selected Entity: <a href="{% url 'horizon:admin:datanets:datanets:detail' datanet.id %}" >{{datanet.name}}</a></h4>
{% endif %}
{% block main %}

View File

@ -20,9 +20,9 @@
<div id="hostTopologyContainer">
<div id="topologyCanvasContainer">
<div class="nodata">{% blocktrans %}There are no hosts or provider networks to display.{% endblocktrans %}</div>
<div class="nodata">{% blocktrans %}There are no hosts or data networks to display.{% endblocktrans %}</div>
{% include "admin/host_topology/_svg_element.html" %}
<div id="detail_view">{% blocktrans %}Select a host or providernet to view its details. The current view can be moved by clicking and dragging.{% endblocktrans %}</div>
<div id="detail_view">{% blocktrans %}Select a host or datanet to view its details. The current view can be moved by clicking and dragging.{% endblocktrans %}</div>
</div>
<span data-hosttopology="{% url 'horizon:admin:host_topology:json' %}" id="hosttopology"></span>
<div id="topologyMessages"></div>

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2016 Wind River Systems, Inc.
# Copyright (c) 2016-2019 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -16,6 +16,6 @@ urlpatterns = [
url(r'^(?P<host_id>[^/]+)/host/$',
views.HostDetailView.as_view(), name='host'),
url(r'^(?P<providernet_id>[^/]+)/providernet/$',
views.ProvidernetDetailView.as_view(), name='providernet')
url(r'^(?P<datanet_id>[^/]+)/datanet/$',
views.DatanetDetailView.as_view(), name='datanet')
]

View File

@ -58,55 +58,56 @@ class HostDetailView(i_views.DetailView):
return self._host
class ProvidernetDetailView(tabs.TabbedTableView):
class DatanetDetailView(tabs.TabbedTableView):
tab_group_class = topology_tabs.ProvidernetDetailTabs
template_name = 'admin/host_topology/detail/tabbed_detail.html'
failure_url = reverse_lazy('horizon:admin:host_topology:index')
def get_context_data(self, **kwargs):
context = super(ProvidernetDetailView, self).get_context_data(**kwargs)
context["providernet"] = self.get_data()
context["nova_providernet"] = self.get_nova_data()
context = super(DatanetDetailView, self).get_context_data(**kwargs)
context["datanet"] = self.get_data()
context["nova_datanet"] = self.get_nova_data()
return context
def get_data(self):
if not hasattr(self, "_providernet"):
if not hasattr(self, "_datanet"):
try:
providernet_id = self.kwargs['providernet_id']
providernet = stx_api.sysinv.data_network_get(
self.request, providernet_id)
datanet_id = self.kwargs['datanet_id']
datanet = stx_api.sysinv.data_network_get(
self.request, datanet_id)
alarms = stx_api.fm.alarm_list(self.request)
# Filter out unrelated alarms
providernet.alarms = \
datanet.alarms = \
topology_tabs.get_alarms_for_entity(alarms,
providernet.id) + \
datanet.id) + \
topology_tabs.get_alarms_for_entity(alarms,
providernet.name)
datanet.name)
# Sort alarms by severity
providernet.alarms.sort(key=lambda a: (a.severity))
datanet.alarms.sort(key=lambda a: (a.severity))
except Exception:
redirect = self.failure_url
exceptions.handle(self.request,
_('Unable to retrieve details for '
'provider network "%s".') % providernet_id,
'provider network "%s".') % datanet_id,
redirect=redirect)
self._providernet = providernet
return self._providernet
self._datanet = datanet
return self._datanet
def get_nova_data(self):
if not hasattr(self, "_providernet_nova"):
# TODO(datanetworks): depends on upstream support
self._providernet_nova = None
return self._providernet_nova
if not hasattr(self, "_datanet_nova"):
# TODO(datanetworks): depends on upstream support for
# nova providernet-show
self._datanet_nova = None
return self._datanet_nova
def get_tabs(self, request, *args, **kwargs):
providernet = self.get_data()
nova_providernet = self.get_nova_data()
datanet = self.get_data()
nova_datanet = self.get_nova_data()
return self.tab_group_class(
request, providernet=providernet,
nova_providernet=nova_providernet, **kwargs)
request, datanet=datanet,
nova_datanet=nova_datanet, **kwargs)
class HostTopologyView(views.HorizonTemplateView):
@ -131,9 +132,6 @@ class HostTopologyView(views.HorizonTemplateView):
def get_context_data(self, **kwargs):
context = super(HostTopologyView, self).get_context_data(**kwargs)
context['launch_instance_allowed'] = self._has_permission(
(("compute", "compute:create"),))
context['instance_quota_exceeded'] = self._quota_exceeded('instances')
return context
@ -206,18 +204,18 @@ class JSONView(View):
data.append(host_data)
return data
def _get_pnets(self, request):
pnets = []
def _get_dnets(self, request):
dnets = []
try:
pnets = stx_api.sysinv.data_network_list(request)
dnets = stx_api.sysinv.data_network_list(request)
except Exception as ex:
exceptions.handle(ex)
data = [p.to_dict() for p in pnets]
data = [p.to_dict() for p in dnets]
return data
def get(self, request, *args, **kwargs):
data = {'hosts': self._get_hosts(request),
'networks': self._get_pnets(request),
'networks': self._get_dnets(request),
'alarms': self._get_alarms(request), }
json_string = json.dumps(data, ensure_ascii=False)
return HttpResponse(json_string, content_type='text/json')

View File

@ -109,7 +109,7 @@ horizon.host_topology = {
$(this).toggle(show_entry);
});
});
// Initialize providernet list sorting behaviour
// Initialize datanet list sorting behaviour
$('#network_list_search').keyup(function(){
var text = $(this).val().toLowerCase();
$('#network_list > a').each(function(){
@ -183,23 +183,23 @@ horizon.host_topology = {
});
host.connections = [];
// 'expand' a single IF connected to many pnets into multiple 'connections'
// 'expand' a single IF connected to many dnets into multiple 'connections'
$.each(host.interfaces, function(index, interface) {
var if_connections = []
if (interface.datanetworks_csv) {
$.each(interface.datanetworks_csv.split(','), function(index, providernet_name) {
if (interface.datanetworks) {
$.each(interface.datanetworks, function(index, datanet_name) {
var connection = {}
// Attach the interface to the connection
connection.interface = interface;
// Loop through networks and attach the full pnet to the connection
$.each(model.networks, function(index, providernet){
if (providernet_name == providernet.name) {
connection.providernet = providernet;
// Loop through networks and attach the full dnet to the connection
$.each(model.networks, function(index, datanet){
if (datanet_name == datanet.name) {
connection.datanet = datanet;
}
});
connection.id = interface.ifname + "-" + providernet_name;
connection.id = interface.ifname + "-" + datanet_name;
// search for and attach lldp info for the port
connection.lldp_labels = [];
@ -233,7 +233,7 @@ horizon.host_topology = {
var hasconns = (host.connections.length <= 0) ? false : true;
main_connection = self.select_main_connection(host.connections);
host.parent_network = (hasconns) ? main_connection.providernet.id : self.model.networks[0].id;
host.parent_network = (hasconns) ? main_connection.datanet.id : self.model.networks[0].id;
var height = element_properties.conn_margin*(host.connections.length - 1);
host.lldp_heights = [];
$.each(host.connections,function(index, connection) {
@ -303,10 +303,10 @@ horizon.host_topology = {
network.hosts.push(host);
}
// Add any hosts with a connection to this pnet (for use in list linking)
// And propagate the pnet's alarm status to the connection
// Add any hosts with a connection to this dnet (for use in list linking)
// And propagate the dnet's alarm status to the connection
$.each(host.connections,function(index, connection) {
if (connection.providernet.name === network.name){
if (connection.datanet.name === network.name){
network.connected_hosts.push(host);
if (network.alarm_level > connection.alarm_level)
connection.alarm_level = network.alarm_level;
@ -348,8 +348,8 @@ horizon.host_topology = {
self.network_height = (self.network_height > element_properties.network_min_height) ?
self.network_height : element_properties.network_min_height;
//console.log(model);
self.draw_topology();
// console.log(model); // Uncomment for console debug logs
self.draw_topology();
self.$loading_template.hide();
},
load_detail:function(spin){
@ -403,7 +403,7 @@ horizon.host_topology = {
}
});
});
// Special functionality on loaded providernet detail view
// Special functionality on loaded datanet detail view
this.$detail_view.find('table#provider_network_ranges tr:not(:first):not(:last)').each(function(i,d) {
$.each(self.selected_entity.alarms, function(index, alarm) {
$(d).removeClass('status_down')
@ -419,7 +419,7 @@ horizon.host_topology = {
if (alarm.alarm_id !== self.segment_alarm_id)
return false; // Unrelated alarm
if (alarm.reason_text.indexOf("ranges") == -1)
return false; // Generic providernetwork alarm, for flat networks
return false; // Generic datanetwork alarm, for flat networks
// Retrieve the csv (with spaces) of failed segment ranges
desc = alarm.reason_text.substring(alarm.reason_text.indexOf("ranges")+7, alarm.reason_text.indexOf(" on host"));
@ -462,7 +462,7 @@ horizon.host_topology = {
}
entry.addClass('active');
$.each(host.connections, function(index, connection) {
connected_entry = $('#network_list a#net-'+connection.providernet.name);
connected_entry = $('#network_list a#net-'+connection.datanet.name);
connected_entry.addClass('related');
connected_entry.prependTo('#network_list');
});
@ -494,7 +494,7 @@ horizon.host_topology = {
connected_entry.prependTo('#host_list');
});
self.detail_url = $(location).attr('href')+network.id+"/providernet/"
self.detail_url = $(location).attr('href')+network.id+"/datanet/"
$.each(self.model.networks, function(index, model_network) {
if (model_network.name === network.name)
self.selected_entity = model_network;
@ -653,7 +653,7 @@ horizon.host_topology = {
.select('.network-name')
.text(function(d) { return d.name; });
// Set the alarm styles for the providernet
// Set the alarm styles for the datanet
network.each(function(d) {
if (d.alarm_level) {
d3.select(this).select('.network-rect-hash').attr('visibility','visible');
@ -774,7 +774,7 @@ horizon.host_topology = {
port.each(function(d,i){
var index_diff = self.get_network_index(this.parentNode._portdata.parent_network) -
self.get_network_index(d.providernet.id);
self.get_network_index(d.datanet.id);
this._index_diff = index_diff = (index_diff >= 0)? ++index_diff : index_diff;
this._direction = (this._index_diff < 0)? 'right' : 'left';
this._index = this.parentNode._portdata[this._direction] ++;
@ -803,7 +803,7 @@ horizon.host_topology = {
return this.parentNode.parentNode._portdata.port_height;
})
.attr('stroke', function(d, i) {
return self.get_network_color(d.providernet.id);
return self.get_network_color(d.datanet.id);
})
.attr('x1',0).attr('y1',0).attr('y2',0)
.attr('x2',function(d,i) {
@ -925,7 +925,7 @@ horizon.host_topology = {
var MAX_INT = 4294967295;
var min_conn_length = MAX_INT;
$.each(connections, function(index, connection){
var conn_length = _self.sum_conn_length(connection.providernet, connections);
var conn_length = _self.sum_conn_length(connection.datanet, connections);
if(conn_length < min_conn_length){
min_conn_length = conn_length;
main_conn_index = index;
@ -933,12 +933,12 @@ horizon.host_topology = {
});
return connections[main_conn_index];
},
sum_conn_length: function(providernet, connections){
sum_conn_length: function(datanet, connections){
var self = this;
var sum_conn_length = 0;
var base_index = self.get_network_index(providernet.id);
var base_index = self.get_network_index(datanet.id);
$.each(connections, function(index, connection){
sum_conn_length += base_index - self.get_network_index(connection.providernet.id);
sum_conn_length += base_index - self.get_network_index(connection.datanet.id);
});
return sum_conn_length;
},