Skip to content
This repository was archived by the owner on Nov 24, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ var FormTopologyController = function(topology, cacheGroups, $anchorScroll, $sco
let removeSecParentReferences = function(topologyTree, secParentName) {
// when a cache group is removed, any references to the cache group as a secParent need to be removed
topologyTree.forEach(function(node) {
if (node.secParent && node.secParent === secParentName) {
node.secParent = '';
if (node.secParent && node.secParent.name === secParentName) {
node.secParent = { name: '', type: '' };
}
if (node.children && node.children.length > 0) {
removeSecParentReferences(node.children, secParentName);
Expand Down Expand Up @@ -74,24 +74,25 @@ var FormTopologyController = function(topology, cacheGroups, $anchorScroll, $sco
return false;
}

// EDGE_LOC cannot have children
if (parent.type === 'EDGE_LOC') {
// EDGE_LOC can only have EDGE_LOC children
if (parent.type === 'EDGE_LOC' && node.type !== 'EDGE_LOC') {
$anchorScroll(); // scrolls window to top
messageModel.setMessages([ { level: 'error', text: 'Cache groups of EDGE_LOC type must not have children.' } ], false);
messageModel.setMessages([ { level: 'error', text: 'EDGE_LOC cache groups can only have EDGE_LOC children.' } ], false);
return false;
}

// update the parent and secParent fields of the node on successful drop
if (parent.cachegroup) {
node.parent = parent.cachegroup; // change the node parent based on where the node is dropped
if (node.parent === node.secParent) {
// change the node parent based on where the node is dropped
node.parent = { name: parent.cachegroup, type: parent.type };
if (node.parent.name === node.secParent.name) {
// node parent and secParent cannot be the same
node.secParent = "";
node.secParent = { name: '', type: '' };
}
} else {
// the node was dropped at the root of the topology. no parents.
node.parent = "";
node.secParent = "";
node.parent = { name: '', type: '' };
node.secParent = { name: '', type: '' };
}
// marks the form as dirty thus enabling the save btn
$scope.topologyForm.dirty.$setDirty();
Expand Down Expand Up @@ -124,22 +125,26 @@ var FormTopologyController = function(topology, cacheGroups, $anchorScroll, $sco
buildCacheGroupNamesInTopology($scope.topologyTree, true);

/* Cache groups that can act as a second parent include:
1. cache groups of type ORG_LOC or MID_LOC (not EDGE_LOC)
2. cache groups that are not currently acting as the primary parent
3. cache groups that exist currently in the topology
1. cache groups that are not the current cache group (you can't parent/sec parent yourself)
2. cache groups that are not currently acting as the primary parent (primary parent != sec parent)
3. cache groups that exist currently in the topology only
4a. any cache group types (ORG_LOC, MID_LOC, EDGE_LOC) if child cache group is EDGE_LOC
4b. only MID_LOC or ORG_LOC cache group types if child cache group is not EDGE_LOC
*/
let eligibleSecParentCandidates = cacheGroups.filter(function(cg) {
return cg.typeName !== 'EDGE_LOC' &&
(node.parent && node.parent !== cg.name) &&
cacheGroupNamesInTopology.includes(cg.name);
return (node.cachegroup && node.cachegroup !== cg.name) &&
(node.parent && node.parent.name !== cg.name) &&
cacheGroupNamesInTopology.includes(cg.name) &&
((node.type === 'EDGE_LOC') || (cg.typeName === 'MID_LOC' || cg.typeName === 'ORG_LOC'));
});

let params = {
title: 'Select a secondary parent',
message: 'Please select a secondary parent that is part of the ' + topology.name + ' topology',
key: 'name',
required: false,
selectedItemKeyValue: node.secParent
selectedItemKeyValue: node.secParent.name,
labelFunction: function(item) { return item['name'] + ' (' + item['typeName'] + ')' }
};
let modalInstance = $uibModal.open({
templateUrl: 'common/modules/dialog/select/dialog.select.tpl.html',
Expand All @@ -156,9 +161,9 @@ var FormTopologyController = function(topology, cacheGroups, $anchorScroll, $sco
});
modalInstance.result.then(function(selectedSecParent) {
if (selectedSecParent) {
node.secParent = selectedSecParent.name;
node.secParent = { name: selectedSecParent.name, type: selectedSecParent.typeName };
} else {
node.secParent = '';
node.secParent = { name: '', type: '' };
}
// marks the form as dirty thus enabling the save btn
$scope.topologyForm.dirty.$setDirty();
Expand All @@ -178,11 +183,23 @@ var FormTopologyController = function(topology, cacheGroups, $anchorScroll, $sco
scope.toggle();
};

$scope.hasNodeError = function(node) {
$scope.nodeError = function(node) {
// only EDGE_LOCs can serve as a leaf node on the topology
if (node.type !== 'EDGE_LOC' && node.children.length === 0) {
return true;
return node.type + ' requires 1+ child cache group';
}
return false;
return '';
};

$scope.nodeWarning = function(node) {
// EDGE_LOCs with parent/secondary parent EDGE_LOCs require special configuration
if (node.parent) {
console.log(node.parent);
}
if ((node.parent && node.parent.type === 'EDGE_LOC') || (node.secParent && node.secParent.type === 'EDGE_LOC')) {
return 'Special Configuration Required';
}
return '';
};

$scope.isOrigin = function(node) {
Expand All @@ -199,50 +216,78 @@ var FormTopologyController = function(topology, cacheGroups, $anchorScroll, $sco

$scope.addCacheGroups = function(parent, scope) {

if (parent.type === 'EDGE_LOC') {
// can't add children to EDGE_LOC. button should be hidden anyhow.
return;
}

// cache groups already in the topology cannot be selected again for addition
buildCacheGroupNamesInTopology($scope.topologyTree, true);

// the types of child cache groups you can add depends on the parent cache group's type
let eligibleChildrenTypes = [ { name: 'EDGE_LOC' } ];
if (parent.type === 'ROOT') {
eligibleChildrenTypes.push({ name: 'MID_LOC' });
eligibleChildrenTypes.push({ name: 'ORG_LOC' });
} else if (parent.type === 'MID_LOC' || parent.type === 'ORG_LOC') {
eligibleChildrenTypes.push({ name: 'MID_LOC' });
}

let parentName = (parent.cachegroup) ? parent.cachegroup : 'ROOT';
let params = {
title: 'Select a child cache group type',
message: 'Please select the type of child cache group(s) you would like to add to ' + parentName,
key: 'name',
};
let modalInstance = $uibModal.open({
templateUrl: 'common/modules/table/topologyCacheGroups/table.selectTopologyCacheGroups.tpl.html',
controller: 'TableSelectTopologyCacheGroupsController',
size: 'lg',
templateUrl: 'common/modules/dialog/select/dialog.select.tpl.html',
controller: 'DialogSelectController',
size: 'md',
resolve: {
parent: function() {
return parent;
},
topology: function() {
return topology;
},
cacheGroups: function(cacheGroupService) {
return cacheGroupService.getCacheGroups();
params: function () {
return params;
},
usedCacheGroupNames: function() {
return cacheGroupNamesInTopology;
collection: function() {
return eligibleChildrenTypes;
}
}
});
modalInstance.result.then(function(result) {
let nodeData = scope.$modelValue,
cacheGroupNodes = result.selectedCacheGroups.map(function(cg) {
return {
id: cg.id,
cachegroup: cg.name,
type: cg.typeName,
parent: (result.parent) ? result.parent : '',
secParent: result.secParent,
children: []
modalInstance.result.then(function(type) {
modalInstance = $uibModal.open({
templateUrl: 'common/modules/table/topologyCacheGroups/table.selectTopologyCacheGroups.tpl.html',
controller: 'TableSelectTopologyCacheGroupsController',
size: 'lg',
resolve: {
parent: function() {
return parent;
},
topology: function() {
return topology;
},
selectedType: function() {
return type.name;
},
cacheGroups: function(cacheGroupService) {
return cacheGroupService.getCacheGroups();
},
usedCacheGroupNames: function() {
return cacheGroupNamesInTopology;
}
}
});
modalInstance.result.then(function(result) {
let nodeData = scope.$modelValue,
cacheGroupNodes = result.selectedCacheGroups.map(function(cg) {
return {
id: cg.id,
cachegroup: cg.name,
type: cg.typeName,
parent: result.parent,
secParent: result.secParent,
children: []
}
});
cacheGroupNodes.forEach(function(node) {
nodeData.children.unshift(node);
});
cacheGroupNodes.forEach(function(node) {
nodeData.children.unshift(node);
// marks the form as dirty thus enabling the save btn
$scope.topologyForm.dirty.$setDirty();
});
// marks the form as dirty thus enabling the save btn
$scope.topologyForm.dirty.$setDirty();
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,25 +68,26 @@

<script type="text/ng-template" id="nodes_renderer.html">
<!-- this hidden form field will make the form invalid (and disable the save btn) because it's a required field with no value -->
<input name="error" ng-if="hasNodeError(node)" ng-model="error" type="hidden" required>
<input name="error" ng-if="nodeError(node)" ng-model="error" type="hidden" required>
<!-- this hidden form field will mark the form as dirty (and enable the save btn) when the topology tree is modified in any way -->
<input name="dirty" ng-model="dirty" type="hidden">
<div id="{{node.cachegroup}}" ui-tree-handle class="tree-node tree-node-content"
ng-class="{ 'error': hasNodeError(node), 'origin': isOrigin(node), 'mid': isMid(node) }">
ng-class="{ 'error': nodeError(node), 'warning': nodeWarning(node), 'origin': isOrigin(node), 'mid': isMid(node) }">
<div class="tree-node-label pull-left">
<a class="tree-toggle btn btn-primary btn-xs" ng-if="node.type !== 'ROOT' && hasChildren(node)" data-nodrag ng-click="toggle(this)">
<i class="fa" ng-class="collapsed ? 'fa-caret-right' : 'fa-caret-down'"></i>
</a> {{::nodeLabel(node)}} <small>{{::node.type}}</small>
</div>
<div ng-if="hasNodeError(node)" class="error-msg">1+ child required for {{::node.type}}</div>
<div ng-if="nodeError(node)" class="error-msg">{{nodeError(node)}}</div>
<div ng-if="nodeWarning(node)" class="error-msg">{{nodeWarning(node)}}</div>
<div class="pull-right">
<a ng-show="node.cachegroup && node.parent" title="Set Secondary Parent Cache Group for {{::node.cachegroup}}" class="btn btn-primary btn-xs" data-nodrag ng-click="editSecParent(node)" style="margin-right: 8px;">
2nd: {{(node.secParent) ? node.secParent : 'None'}}
2nd Parent: {{(node.secParent.name) ? node.secParent.name : ''}} [{{node.secParent.type}}]
</a>
<a ng-if="node.cachegroup" title="View Servers Assigned to {{::node.cachegroup}}" class="btn btn-primary btn-xs" data-nodrag ng-click="viewCacheGroupServers(node)" style="margin-right: 8px;">
<i class="fa fa-server"></i>
</a>
<a ng-disabled="node.type === 'EDGE_LOC'" title="{{(node.type !== 'EDGE_LOC') ? 'Add child cache groups to ' + nodeLabel(node) : 'Child cache groups cannot be added to EDGE_LOC cache groups'}}" class="btn btn-primary btn-xs" data-nodrag ng-click="addCacheGroups(node, this)" style="margin-right: 8px;">
<a title="Add child cache groups to {{nodeLabel(node)}}" class="btn btn-primary btn-xs" data-nodrag ng-click="addCacheGroups(node, this)" style="margin-right: 8px;">
<i class="fa fa-plus"></i>
</a>
<a ng-if="node.cachegroup" title="Remove {{::node.cachegroup}} Cache Group" class="btn btn-danger btn-xs" data-nodrag ng-click="deleteCacheGroup(node, this)">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* under the License.
*/

var TableSelectTopologyCacheGroupsController = function(parent, topology, cacheGroups, usedCacheGroupNames, $scope, $uibModal, $uibModalInstance, serverService) {
var TableSelectTopologyCacheGroupsController = function(parent, topology, selectedType, cacheGroups, usedCacheGroupNames, $scope, $uibModal, $uibModalInstance, serverService) {

let selectedCacheGroups = [],
usedCacheGroupCount = 0;
Expand Down Expand Up @@ -59,10 +59,7 @@ var TableSelectTopologyCacheGroupsController = function(parent, topology, cacheG
$scope.parent = parent;

$scope.cacheGroups = cacheGroups.filter(function(cg) {
// all cg types (ORG_LOC, MID_LOC, EDGE_LOC) can be added to the root of a topology
// but only EDGE_LOC and MID_LOC can be added farther down the topology tree
if (parent.type === 'ROOT') return (cg.typeName === 'EDGE_LOC' || cg.typeName === 'MID_LOC' || cg.typeName === 'ORG_LOC');
return (cg.typeName === 'EDGE_LOC' || cg.typeName === 'MID_LOC');
return cg.typeName === selectedType;
});

$scope.selectAll = function($event) {
Expand Down Expand Up @@ -101,19 +98,25 @@ var TableSelectTopologyCacheGroupsController = function(parent, topology, cacheG
};

$scope.submit = function() {
// cache groups that are eligible to be a secondary parent include cache groups that are:
/* Cache groups that can act as a second parent include:
1. cache groups that are not currently acting as the primary parent
2. cache groups that exist currently in the topology
3a. any cache group types (ORG_LOC, MID_LOC, EDGE_LOC) if child cache group(s) are EDGE_LOC
3b. only MID_LOC or ORG_LOC cache group types if child cache group(s) are MID_LOC or ORG_LOC
*/
let eligibleSecParentCandidates = cacheGroups.filter(function(cg) {
return cg.typeName !== 'EDGE_LOC' && // not an edge_loc cache group
(parent.cachegroup && parent.cachegroup !== cg.name) && // not the primary parent cache group
usedCacheGroupNames.includes(cg.name); // a cache group that exists in the topology
return (parent.cachegroup && parent.cachegroup !== cg.name) &&
usedCacheGroupNames.includes(cg.name) &&
((selectedType === 'EDGE_LOC') || (cg.typeName === 'MID_LOC' || cg.typeName === 'ORG_LOC'));
});

if (eligibleSecParentCandidates.length === 0) {
$uibModalInstance.close({ selectedCacheGroups: selectedCacheGroups, parent: parent.cachegroup, secParent: '' });
$uibModalInstance.close({ selectedCacheGroups: selectedCacheGroups, parent: { name: parent.cachegroup, type: parent.type }, secParent: { name: '', type: ''} });
return;
}
let params = {
title: 'Assign secondary parent?',
message: 'Would you like to assign a secondary parent to the following cache groups?<br><br>primary parent = ' + parent.cachegroup + '<br><br>'
message: 'Primary parent = ' + parent.cachegroup + '<br>Secondary parent = null<br><br>Would you like to assign a secondary parent to the following cache groups? Note: secondary parent assignment is optional and can be done later.<br><br>'
};
params.message += selectedCacheGroups.map(function(cg) { return cg.name }).join('<br>') + '<br><br>';
let modalInstance = $uibModal.open({
Expand All @@ -131,7 +134,8 @@ var TableSelectTopologyCacheGroupsController = function(parent, topology, cacheG
let params = {
title: 'Select a secondary parent',
message: 'Please select a secondary parent that is part of the ' + topology.name + ' topology',
key: 'name'
key: 'name',
labelFunction: function(item) { return item['name'] + ' (' + item['typeName'] + ')' }
};
let modalInstance = $uibModal.open({
templateUrl: 'common/modules/dialog/select/dialog.select.tpl.html',
Expand All @@ -149,14 +153,14 @@ var TableSelectTopologyCacheGroupsController = function(parent, topology, cacheG
});
modalInstance.result.then(function(cg) {
// user selected a secondary parent
$uibModalInstance.close({ selectedCacheGroups: selectedCacheGroups, parent: parent.cachegroup, secParent: cg.name });
$uibModalInstance.close({ selectedCacheGroups: selectedCacheGroups, parent: { name: parent.cachegroup, type: parent.type }, secParent: { name: cg.name, type: cg.typeName } });
}, function () {
// user apparently changed their mind and doesn't want to select a secondary parent
$uibModalInstance.close({ selectedCacheGroups: selectedCacheGroups, parent: parent.cachegroup, secParent: '' });
$uibModalInstance.close({ selectedCacheGroups: selectedCacheGroups, parent: { name: parent.cachegroup, type: parent.type }, secParent: { name: '', type: ''} });
});
}, function () {
// user doesn't want to select a secondary parent
$uibModalInstance.close({ selectedCacheGroups: selectedCacheGroups, parent: parent.cachegroup, secParent: '' });
$uibModalInstance.close({ selectedCacheGroups: selectedCacheGroups, parent: { name: parent.cachegroup, type: parent.type }, secParent: { name: '', type: ''} });
});
};

Expand Down Expand Up @@ -190,5 +194,5 @@ var TableSelectTopologyCacheGroupsController = function(parent, topology, cacheG

};

TableSelectTopologyCacheGroupsController.$inject = ['parent', 'topology', 'cacheGroups', 'usedCacheGroupNames', '$scope', '$uibModal', '$uibModalInstance', 'serverService'];
TableSelectTopologyCacheGroupsController.$inject = ['parent', 'topology', 'selectedType', 'cacheGroups', 'usedCacheGroupNames', '$scope', '$uibModal', '$uibModalInstance', 'serverService'];
module.exports = TableSelectTopologyCacheGroupsController;
14 changes: 8 additions & 6 deletions traffic_portal/app/src/common/service/utils/TopologyUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ var TopologyUtils = function() {

let addNodeIndexes = function() {
normalizedTopology.nodes.forEach(function(currentNode) {
let parentNodeIndex = _.findIndex(normalizedTopology.nodes, function(node) { return currentNode.parent === node.cachegroup });
let secParentNodeIndex = _.findIndex(normalizedTopology.nodes, function(node) { return currentNode.secParent === node.cachegroup });
let parentNodeIndex = _.findIndex(normalizedTopology.nodes, function(node) { return currentNode.parent.name === node.cachegroup });
let secParentNodeIndex = _.findIndex(normalizedTopology.nodes, function(node) { return currentNode.secParent.name === node.cachegroup });
if (parentNodeIndex > -1) {
currentNode.parents.push(parentNodeIndex);
if (secParentNodeIndex > -1) {
Expand Down Expand Up @@ -79,8 +79,8 @@ var TopologyUtils = function() {
item.children = []
}
if (item.parents.length === 0) {
item.parent = "";
item.secParent = "";
item.parent = { name: '', type: '' };
item.secParent = { name: '', type: '' };
roots.push(item)
} else if (item.parents[0] in all) {
let p = all[item.parents[0]]
Expand All @@ -89,10 +89,12 @@ var TopologyUtils = function() {
}
p.children.push(item);
// add parent to each node
item.parent = all[item.parents[0]].cachegroup;
item.parent = { name: all[item.parents[0]].cachegroup, type: all[item.parents[0]].type };
// add secParent to each node
if (item.parents.length === 2 && item.parents[1] in all) {
item.secParent = all[item.parents[1]].cachegroup;
item.secParent = { name: all[item.parents[1]].cachegroup, type: all[item.parents[1]].type };
} else {
item.secParent = { name: '', type: '' };
}
}
});
Expand Down
5 changes: 5 additions & 0 deletions traffic_portal/app/src/styles/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -220,5 +220,10 @@ input[type="checkbox"].dirty {
background-color: #f2dede;
border-color: #f5c6cb;
}
.tree-node.warning {
color: #856404;
background-color: #fff3cd;
border-color: #ffeeba;
}

}