mirror of
https://github.com/ansible/awx.git
synced 2024-11-01 16:51:11 +03:00
Merge pull request #6784 from marshmalien/rampartsViews
Implementation of Instance Groups read-only views in Tower Settings
This commit is contained in:
commit
193f3fa10e
@ -60,7 +60,7 @@ table, tbody {
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
color: @list-item;
|
||||
border-bottom: 1px solid @default-white-button-bord;
|
||||
border-bottom: 1px solid @default-border;
|
||||
}
|
||||
|
||||
.List-tableRow:last-of-type {
|
||||
@ -176,6 +176,27 @@ table, tbody {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.List-exitHolder {
|
||||
justify-content: flex-end;
|
||||
display:flex;
|
||||
}
|
||||
|
||||
.List-exit {
|
||||
cursor:pointer;
|
||||
padding:0px;
|
||||
border: none;
|
||||
height:20px;
|
||||
font-size: 20px;
|
||||
background-color:@default-bg;
|
||||
color:@d7grey;
|
||||
transition: color 0.2s;
|
||||
line-height:1;
|
||||
}
|
||||
|
||||
.List-exit:hover{
|
||||
color:@default-icon;
|
||||
}
|
||||
|
||||
.List-actionHolder {
|
||||
justify-content: flex-end;
|
||||
display: flex;
|
||||
|
@ -34,6 +34,13 @@
|
||||
.BreadCrumb-menuLink:hover {
|
||||
color: @bc-link-icon-focus;
|
||||
}
|
||||
.BreadCrumb-menuLink {
|
||||
.BreadCrumb-menuLinkImage.fa-refresh {
|
||||
&:hover {
|
||||
color: @default-link;
|
||||
}
|
||||
}
|
||||
}
|
||||
.BreadCrumb-menuLinkImage {
|
||||
font-size: 18px;
|
||||
color: @bc-link-icon;
|
||||
|
@ -12,6 +12,7 @@ export default
|
||||
|
||||
scope.showActivityStreamButton = false;
|
||||
scope.showRefreshButton = false;
|
||||
scope.alwaysShowRefreshButton = false;
|
||||
scope.loadingLicense = true;
|
||||
|
||||
scope.$on("$stateChangeSuccess", function updateActivityStreamButton(event, toState, toParams, fromState, fromParams) {
|
||||
@ -48,6 +49,7 @@ export default
|
||||
}
|
||||
|
||||
scope.showRefreshButton = (streamConfig && streamConfig.refreshButton) ? true : false;
|
||||
scope.alwaysShowRefreshButton = (streamConfig && streamConfig.alwaysShowRefreshButton) ? true: false;
|
||||
});
|
||||
|
||||
// scope.$on('featuresLoaded', function(){
|
||||
|
@ -8,7 +8,7 @@
|
||||
data-trigger="hover"
|
||||
data-container="body"
|
||||
ng-hide= "loadingLicense || licenseMissing"
|
||||
ng-if="socketStatus === 'error' && showRefreshButton"
|
||||
ng-if="(socketStatus === 'error' && showRefreshButton) || alwaysShowRefreshButton"
|
||||
ng-click="refresh()">
|
||||
<i class="BreadCrumb-menuLinkImage fa fa-refresh"
|
||||
alt="Refresh the page">
|
||||
|
@ -0,0 +1,28 @@
|
||||
@import "../../shared/branding/colors.default.less";
|
||||
|
||||
capacity-bar {
|
||||
|
||||
width: 50%;
|
||||
margin-right: 10px;
|
||||
min-width: 100px;
|
||||
|
||||
.CapacityBar {
|
||||
background-color: @default-bg;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
height: 10px;
|
||||
border: 1px solid @default-link;
|
||||
width: 100%;
|
||||
border-radius: 100vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.CapacityBar-remaining {
|
||||
background-color: @default-link;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.CapacityBar-consumed {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
export default ['templateUrl',
|
||||
function (templateUrl) {
|
||||
return {
|
||||
scope: {
|
||||
capacity: '='
|
||||
},
|
||||
templateUrl: templateUrl('instance-groups/capacity-bar/capacity-bar'),
|
||||
restrict: 'E',
|
||||
link: function(scope) {
|
||||
scope.$watch('capacity', function() {
|
||||
scope.PercentRemainingStyle = {
|
||||
'flex-grow': scope.capacity * 0.01
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
];
|
@ -0,0 +1,4 @@
|
||||
<div class="CapacityBar">
|
||||
<div class="CapacityBar-remaining" ng-style="PercentRemainingStyle"></div>
|
||||
<div class="CapacityBar-consumed"></div>
|
||||
</div>
|
5
awx/ui/client/src/instance-groups/capacity-bar/main.js
Normal file
5
awx/ui/client/src/instance-groups/capacity-bar/main.js
Normal file
@ -0,0 +1,5 @@
|
||||
import capacityBar from './capacity-bar.directive';
|
||||
|
||||
export default
|
||||
angular.module('capacityBarDirective', [])
|
||||
.directive('capacityBar', capacityBar);
|
56
awx/ui/client/src/instance-groups/instance-group.block.less
Normal file
56
awx/ui/client/src/instance-groups/instance-group.block.less
Normal file
@ -0,0 +1,56 @@
|
||||
@import "../shared/branding/colors.default.less";
|
||||
|
||||
.InstanceGroups {
|
||||
|
||||
.BreadCrumb-menuLinkImage:hover {
|
||||
color: @default-link;
|
||||
}
|
||||
|
||||
.List-details {
|
||||
align-self: flex-end;
|
||||
color: @default-interface-txt;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
font-size: 12px;
|
||||
margin-right:20px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.Capacity-details {
|
||||
display: flex;
|
||||
margin-right: 20px;
|
||||
align-items: center;
|
||||
|
||||
.Capacity-details--label {
|
||||
color: @default-interface-txt;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.Capacity-details--percentage {
|
||||
color: @default-data-txt;
|
||||
}
|
||||
}
|
||||
|
||||
.RunningJobs-details {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
.RunningJobs-details--label {
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.List-tableCell--capacityRemainingColumn {
|
||||
display: flex;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.List-noItems {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.List-tableRow .List-titleBadge {
|
||||
margin: 0 0 0 5px;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<div class="Panel">
|
||||
<div class="row Form-tabRow">
|
||||
<div class="col-xs-12">
|
||||
<div class="List-header">
|
||||
<div class="List-title">
|
||||
<div class="List-titleText">{{ instanceGroupName }}</div>
|
||||
</div>
|
||||
<div class="List-details">
|
||||
<div class="Capacity-details">
|
||||
<p class="Capacity-details--label" translate>Capacity</p>
|
||||
<capacity-bar capacity="instanceGroupCapacity"></capacity-bar>
|
||||
<span class="Capacity-details--percentage">{{ instanceGroupCapacity }}%</span>
|
||||
</div>
|
||||
<div class="RunningJobs-details">
|
||||
<p class="RunningJobs-details--label" translate>Running Jobs</p>
|
||||
<span class="badge List-titleBadge">
|
||||
{{ instanceGroupJobsRunning }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="List-exitHolder">
|
||||
<button class="List-exit" ng-click="$state.go('instanceGroups')">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Form-tabHolder">
|
||||
<div class="Form-tab Form-tab--notitle" ng-click="$state.go('instanceGroups.instances.list', {instance_group_id: $stateParams.instance_group_id})" ng-class="{'is-selected': $state.includes('instanceGroups.instances.list')}" translate>INSTANCES</div>
|
||||
<div class="Form-tab Form-tab--notitle" ng-click="$state.go('instanceGroups.instances.jobs', {instance_group_id: $stateParams.instance_group_id})" ng-class="{'is-selected': $state.includes('instanceGroups.instances.jobs')}" translate>JOBS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ui-view="list"></div>
|
||||
</div>
|
@ -1,4 +0,0 @@
|
||||
#InstanceGroups {
|
||||
display: flex;
|
||||
padding: 0 12px;
|
||||
}
|
@ -14,8 +14,10 @@ export default ['i18n', function(i18n) {
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-md-3 col-sm-9 col-xs-9',
|
||||
modalColumnClass: 'col-md-8',
|
||||
uiSref: 'instanceGroups.instances.list({instance_group_id: instance_group.id})',
|
||||
ngClass: "{'isActive' : isActive()}"
|
||||
},
|
||||
capacity: {
|
||||
percent_capacity_remaining: {
|
||||
label: i18n._('Capacity'),
|
||||
nosort: true,
|
||||
},
|
||||
|
@ -0,0 +1,11 @@
|
||||
<div class="tab-pane InstanceGroups" id="instance-groups-panel">
|
||||
<aw-limit-panels max-panels="2" panel-container="instance-groups-panel"></aw-limit-panels>
|
||||
|
||||
<div ui-view="instanceJobs"></div>
|
||||
|
||||
<div ui-view="instances"></div>
|
||||
|
||||
<div ng-cloak id="htmlTemplate" class="Panel">
|
||||
<div ui-view="list"></div>
|
||||
</div>
|
||||
</div>
|
41
awx/ui/client/src/instance-groups/instance-groups.route.js
Normal file
41
awx/ui/client/src/instance-groups/instance-groups.route.js
Normal file
@ -0,0 +1,41 @@
|
||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default {
|
||||
name: 'instanceGroups',
|
||||
url: '/instance_groups',
|
||||
searchPrefix: 'instance_group',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: N_('INSTANCE GROUPS')
|
||||
},
|
||||
params: {
|
||||
instance_group_search: {
|
||||
value: {
|
||||
page_size: '10',
|
||||
order_by: 'name'
|
||||
}
|
||||
}
|
||||
},
|
||||
data: {
|
||||
alwaysShowRefreshButton: true,
|
||||
},
|
||||
views: {
|
||||
'@': {
|
||||
templateUrl: templateUrl('./instance-groups/instance-groups'),
|
||||
},
|
||||
'list@instanceGroups': {
|
||||
templateUrl: templateUrl('./instance-groups/list/instance-groups-list'),
|
||||
controller: 'InstanceGroupsList'
|
||||
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['InstanceGroupList', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
function(list, qs, $stateParams, GetBasePath) {
|
||||
let path = GetBasePath(list.basePath) || GetBasePath(list.name);
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
@ -0,0 +1,40 @@
|
||||
import { N_ } from '../../../i18n';
|
||||
|
||||
export default {
|
||||
name: 'instanceGroups.instances.list.job.list',
|
||||
url: '/jobs',
|
||||
searchPrefix: 'instance_job',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'instanceGroups.instances.list',
|
||||
label: N_('{{ breadcrumb.instance_name }}')
|
||||
},
|
||||
params: {
|
||||
instance_job_search: {
|
||||
value: {
|
||||
page_size: '10',
|
||||
order_by: '-finished',
|
||||
not__launch_type: 'sync'
|
||||
}
|
||||
}
|
||||
},
|
||||
views: {
|
||||
'list@instanceGroups.instances.list.job': {
|
||||
templateProvider: function(InstanceJobsList, generateList) {
|
||||
let html = generateList.build({
|
||||
list: InstanceJobsList
|
||||
});
|
||||
return html;
|
||||
},
|
||||
controller: 'InstanceJobsController'
|
||||
}
|
||||
},
|
||||
|
||||
resolve: {
|
||||
Dataset: ['InstanceJobsList', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
function(list, qs, $stateParams, GetBasePath) {
|
||||
let path = `${GetBasePath('instances')}${$stateParams.instance_id}/jobs`;
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
}
|
||||
],
|
||||
}
|
||||
};
|
@ -0,0 +1,82 @@
|
||||
export default ['$scope','InstanceJobsList', 'GetBasePath', 'Rest', 'Dataset','Find', '$state', '$q',
|
||||
function($scope, InstanceJobsList, GetBasePath, Rest, Dataset, Find, $state, $q) {
|
||||
|
||||
let list = InstanceJobsList;
|
||||
|
||||
init();
|
||||
|
||||
function init(){
|
||||
$scope.optionsDefer = $q.defer();
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
}
|
||||
|
||||
$scope.$on(`${list.iterator}_options`, function(event, data){
|
||||
$scope.options = data.data.actions.GET;
|
||||
optionsRequestDataProcessing();
|
||||
});
|
||||
|
||||
// iterate over the list and add fields like type label, after the
|
||||
// OPTIONS request returns, or the list is sorted/paginated/searched
|
||||
function optionsRequestDataProcessing(){
|
||||
|
||||
if($scope[list.name] && $scope[list.name].length > 0) {
|
||||
$scope[list.name].forEach(function(item, item_idx) {
|
||||
var itm = $scope[list.name][item_idx];
|
||||
|
||||
if(item.summary_fields && item.summary_fields.source_workflow_job &&
|
||||
item.summary_fields.source_workflow_job.id){
|
||||
item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`;
|
||||
}
|
||||
|
||||
// Set the item type label
|
||||
if (list.fields.type && $scope.options &&
|
||||
$scope.options.hasOwnProperty('type')) {
|
||||
$scope.options.type.choices.forEach(function(choice) {
|
||||
if (choice[0] === item.type) {
|
||||
itm.type_label = choice[1];
|
||||
}
|
||||
});
|
||||
}
|
||||
buildTooltips(itm);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function buildTooltips(job) {
|
||||
job.status_tip = 'Job ' + job.status + ". Click for details.";
|
||||
}
|
||||
|
||||
$scope.viewjobResults = function(job) {
|
||||
var goTojobResults = function(state) {
|
||||
$state.go(state, { id: job.id }, { reload: true });
|
||||
};
|
||||
switch (job.type) {
|
||||
case 'job':
|
||||
goTojobResults('jobResult');
|
||||
break;
|
||||
case 'ad_hoc_command':
|
||||
goTojobResults('adHocJobStdout');
|
||||
break;
|
||||
case 'system_job':
|
||||
goTojobResults('managementJobStdout');
|
||||
break;
|
||||
case 'project_update':
|
||||
goTojobResults('scmUpdateStdout');
|
||||
break;
|
||||
case 'inventory_update':
|
||||
goTojobResults('inventorySyncStdout');
|
||||
break;
|
||||
case 'workflow_job':
|
||||
goTojobResults('workflowResults');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.$watchCollection(`${$scope.list.name}`, function() {
|
||||
optionsRequestDataProcessing();
|
||||
}
|
||||
);
|
||||
}
|
||||
];
|
@ -0,0 +1,78 @@
|
||||
export default ['i18n', function(i18n) {
|
||||
return {
|
||||
|
||||
name: 'instance_jobs',
|
||||
iterator: 'instance_job',
|
||||
index: false,
|
||||
hover: false,
|
||||
well: false,
|
||||
emptyListText: i18n._('No jobs have yet run.'),
|
||||
title: false,
|
||||
basePath: 'api/v2/instances/{{$stateParams.instance_id}}/jobs',
|
||||
|
||||
fields: {
|
||||
status: {
|
||||
label: '',
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumn--smallStatus',
|
||||
dataTipWatch: 'instance_job.status_tip',
|
||||
awToolTip: "{{ instance_job.status_tip }}",
|
||||
awTipPlacement: "right",
|
||||
dataTitle: "{{ instance_job.status_popover_title }}",
|
||||
icon: 'icon-job-{{ instance_job.status }}',
|
||||
iconOnly: true,
|
||||
ngClick:"viewjobResults(instance_job)",
|
||||
nosort: true
|
||||
},
|
||||
id: {
|
||||
label: i18n._('ID'),
|
||||
ngClick:"viewjobResults(instance_job)",
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
||||
awToolTip: "{{ instance_job.status_tip }}",
|
||||
dataPlacement: 'top',
|
||||
noLink: true
|
||||
},
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6',
|
||||
ngClick: "viewjobResults(instance_job)",
|
||||
nosort: true,
|
||||
badgePlacement: 'right',
|
||||
badgeCustom: true,
|
||||
badgeIcon: `<a href="{{ instance_job.workflow_result_link }}"
|
||||
aw-tool-tip="{{'View workflow results'|translate}}"
|
||||
data-placement="top"
|
||||
data-original-title="" title="">
|
||||
<i class="WorkflowBadge"
|
||||
ng-show="instance_job.launch_type === 'workflow' ">
|
||||
W
|
||||
</i>
|
||||
</a>`
|
||||
},
|
||||
type: {
|
||||
label: i18n._('Type'),
|
||||
ngBind: 'instance_job.type_label',
|
||||
link: false,
|
||||
columnClass: "col-lg-2 hidden-md hidden-sm hidden-xs",
|
||||
nosort: true
|
||||
},
|
||||
finished: {
|
||||
label: i18n._('Finished'),
|
||||
noLink: true,
|
||||
filter: "longDate",
|
||||
columnClass: "col-lg-2 col-md-3 col-sm-3 hidden-xs",
|
||||
key: true,
|
||||
desc: true,
|
||||
nosort: true
|
||||
},
|
||||
labels: {
|
||||
label: i18n._('Labels'),
|
||||
type: 'labels',
|
||||
nosort: true,
|
||||
showDelete: false,
|
||||
columnClass: 'List-tableCell col-lg-4 col-md-4 hidden-sm hidden-xs',
|
||||
sourceModel: 'labels',
|
||||
sourceField: 'name',
|
||||
},
|
||||
}
|
||||
};
|
||||
}];
|
@ -0,0 +1,33 @@
|
||||
<div class="Panel">
|
||||
<div class="row Form-tabRow">
|
||||
<div class="col-xs-12">
|
||||
<div class="List-header">
|
||||
<div class="List-title">
|
||||
<div class="List-titleText">{{ instanceName }}</div>
|
||||
</div>
|
||||
<div class="List-details">
|
||||
<div class="Capacity-details">
|
||||
<p class="Capacity-details--label" translate>Capacity</p>
|
||||
<capacity-bar capacity="instanceCapacity"></capacity-bar>
|
||||
<span class="Capacity-details--percentage">{{ instanceCapacity }}%</span>
|
||||
</div>
|
||||
<div class="RunningJobs-details">
|
||||
<p class="RunningJobs-details--label" translate>Running Jobs</p>
|
||||
<span class="badge List-titleBadge">
|
||||
{{ instanceJobsRunning }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="List-exitHolder">
|
||||
<button class="List-exit" ng-click="$state.go('instanceGroups.instances.list')">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Form-tabHolder">
|
||||
<div class="Form-tab Form-tab--notitle is-selected" translate>JOBS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="instance-jobs-list" ui-view="list"></div>
|
||||
</div>
|
@ -0,0 +1,37 @@
|
||||
import { templateUrl } from '../../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'instanceGroups.instances.list.job',
|
||||
url: '/:instance_id',
|
||||
abstract: true,
|
||||
ncyBreadcrumb: {
|
||||
skip: true
|
||||
},
|
||||
views: {
|
||||
'instanceJobs@instanceGroups': {
|
||||
templateUrl: templateUrl('./instance-groups/instances/instance-jobs/instance-jobs'),
|
||||
controller: function($scope, $rootScope, instance) {
|
||||
$scope.instanceName = instance.hostname;
|
||||
$scope.instanceCapacity = instance.percent_capacity_remaining;
|
||||
$scope.instanceJobsRunning = instance.jobs_running;
|
||||
$rootScope.breadcrumb.instance_name = instance.hostname;
|
||||
}
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
instance: ['GetBasePath', 'Rest', 'ProcessErrors', '$stateParams', function(GetBasePath, Rest, ProcessErrors, $stateParams) {
|
||||
let url = GetBasePath('instances') + $stateParams.instance_id;
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.then(({data}) => {
|
||||
return data;
|
||||
})
|
||||
.catch(({data, status}) => {
|
||||
ProcessErrors(null, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get instance groups info. GET returned status: ' + status
|
||||
});
|
||||
});
|
||||
}]
|
||||
}
|
||||
};
|
@ -0,0 +1,43 @@
|
||||
<div class="instances-list">
|
||||
<smart-search django-model="instances" base-path="instances" iterator="instance" dataset="instance_dataset"
|
||||
list="list" collection="instances" search-tags="searchTags">
|
||||
</smart-search>
|
||||
|
||||
<div class="List-noItems ng-hide" ng-show="instances.length === 0 && (searchTags | isEmpty)" translate>PLEASE ADD ITEMS TO THIS LIST</div>
|
||||
<div class="list-table-container" ng-show="instances.length > 0">
|
||||
<table id="instances_table" class="List-table" is-extended="false">
|
||||
<thead>
|
||||
<tr class="List-tableHeaderRow">
|
||||
<th id="instance-hostname-header" class="List-tableHeader list-header col-md-5 col-sm-5 col-xs-5" ng-click="columnNoSort !== 'true' && toggleColumnOrderBy()"
|
||||
ng-class="{'list-header-noSort' : columnNoSort === 'true'}" base-path="instances" collection="instances"
|
||||
dataset="instance_dataset" column-sort="" column-field="hostname" column-iterator="instance" column-no-sort="undefined"
|
||||
column-label="Name" column-custom-class="col-md-5 col-sm-5 col-xs-5" query-set="instance_queryset">
|
||||
"{{'Name' | translate}}"
|
||||
<i ng-hide="columnNoSort === 'true'" class="fa columnSortIcon fa-sort-up" ng-class="orderByIcon()"></i>
|
||||
</th>
|
||||
<th id="instance-percent_capacity_remaining-header" class="List-tableHeader list-header list-header-noSort" translate>
|
||||
Capacity
|
||||
</th>
|
||||
<th id="instance-jobs_running-header" class="List-tableHeader list-header list-header-noSort" translate>
|
||||
Running Jobs
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- ngRepeat: instance in instances -->
|
||||
<tr ng-class="{isActive: isActive(instance.id)}" id="instance.id" class="List-tableRow instance_class ng-scope" ng-repeat="instance in instances">
|
||||
<td class="List-tableCell hostname-column col-md-5 col-sm-5 col-xs-5">
|
||||
<a ui-sref="instanceGroups.instances.list.job.list({instance_id: instance.id})" class="ng-binding">{{ instance.hostname }}</a></td>
|
||||
<td class="List-tableCell List-tableCell--capacityRemainingColumn ng-binding">
|
||||
<capacity-bar capacity="instance.percent_capacity_remaining"></capacity-bar><span>{{ instance.percent_capacity_remaining }}%</span>
|
||||
</td>
|
||||
<td class="List-tableCell jobs_running-column ng-binding">
|
||||
<a ui-sref="instanceGroups.instances.jobs({instance_group_id: $stateParams.instance_group_id})">
|
||||
{{ instance.jobs_running }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,34 @@
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
import { N_ } from '../../i18n';
|
||||
|
||||
export default {
|
||||
name: 'instanceGroups.instances.list',
|
||||
url: '/instances',
|
||||
searchPrefix: 'instance',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'instanceGroups',
|
||||
label: N_('{{breadcrumb.instance_group_name}}')
|
||||
},
|
||||
params: {
|
||||
instance_search: {
|
||||
value: {
|
||||
page_size: '10',
|
||||
order_by: 'hostname'
|
||||
}
|
||||
}
|
||||
},
|
||||
views: {
|
||||
'list@instanceGroups.instances': {
|
||||
templateUrl: templateUrl('./instance-groups/instances/instances-list'),
|
||||
controller: 'InstanceListController'
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['InstanceList', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
function(list, qs, $stateParams, GetBasePath) {
|
||||
let path = `${GetBasePath('instance_groups')}${$stateParams.instance_group_id}/instances`;
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
@ -0,0 +1,20 @@
|
||||
export default ['$scope', 'InstanceList', 'GetBasePath', 'Rest', 'Dataset','Find', '$state', '$q',
|
||||
function($scope, InstanceList, GetBasePath, Rest, Dataset, Find, $state, $q) {
|
||||
let list = InstanceList;
|
||||
|
||||
init();
|
||||
|
||||
function init(){
|
||||
$scope.optionsDefer = $q.defer();
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
}
|
||||
|
||||
$scope.isActive = function(id) {
|
||||
let selected = parseInt($state.params.instance_id);
|
||||
return id === selected;
|
||||
};
|
||||
|
||||
}
|
||||
];
|
@ -0,0 +1,29 @@
|
||||
export default ['i18n', function(i18n) {
|
||||
return {
|
||||
name: 'instances' ,
|
||||
iterator: 'instance',
|
||||
listTitle: false,
|
||||
index: false,
|
||||
hover: false,
|
||||
tabs: true,
|
||||
well: true,
|
||||
|
||||
fields: {
|
||||
hostname: {
|
||||
key: true,
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-md-3 col-sm-9 col-xs-9',
|
||||
modalColumnClass: 'col-md-8',
|
||||
uiSref: 'instanceGroups.instances.list.job({instance_id: instance.id})'
|
||||
},
|
||||
percent_capacity_remaining: {
|
||||
label: i18n._('Capacity'),
|
||||
nosort: true,
|
||||
},
|
||||
jobs_running: {
|
||||
label: i18n._('Running Jobs'),
|
||||
nosort: true,
|
||||
},
|
||||
}
|
||||
};
|
||||
}];
|
@ -0,0 +1,34 @@
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
name: 'instanceGroups.instances',
|
||||
url: '/:instance_group_id',
|
||||
abstract: true,
|
||||
views: {
|
||||
'instances@instanceGroups': {
|
||||
templateUrl: templateUrl('./instance-groups/instance-group'),
|
||||
controller: function($scope, $rootScope, instanceGroup) {
|
||||
$scope.instanceGroupName = instanceGroup.name;
|
||||
$scope.instanceGroupCapacity = instanceGroup.percent_capacity_remaining;
|
||||
$scope.instanceGroupJobsRunning = instanceGroup.jobs_running;
|
||||
$rootScope.breadcrumb.instance_group_name = instanceGroup.name;
|
||||
}
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
instanceGroup: ['GetBasePath', 'Rest', 'ProcessErrors', '$stateParams', function(GetBasePath, Rest, ProcessErrors, $stateParams) {
|
||||
let url = GetBasePath('instance_groups') + $stateParams.instance_group_id;
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.then(({data}) => {
|
||||
return data;
|
||||
})
|
||||
.catch(({data, status}) => {
|
||||
ProcessErrors(null, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get instance groups info. GET returned status: ' + status
|
||||
});
|
||||
});
|
||||
}]
|
||||
}
|
||||
};
|
40
awx/ui/client/src/instance-groups/jobs/jobs-list.route.js
Normal file
40
awx/ui/client/src/instance-groups/jobs/jobs-list.route.js
Normal file
@ -0,0 +1,40 @@
|
||||
import { N_ } from '../../i18n';
|
||||
|
||||
export default {
|
||||
name: 'instanceGroups.instances.jobs',
|
||||
url: '/jobs',
|
||||
searchPrefix: 'job',
|
||||
ncyBreadcrumb: {
|
||||
parent: 'instanceGroups.instances.list',
|
||||
label: N_('JOBS')
|
||||
},
|
||||
params: {
|
||||
job_search: {
|
||||
value: {
|
||||
page_size: '10',
|
||||
order_by: '-finished',
|
||||
not__launch_type: 'sync'
|
||||
}
|
||||
},
|
||||
instance_group_id: null
|
||||
},
|
||||
views: {
|
||||
'list@instanceGroups.instances': {
|
||||
templateProvider: function(JobsList, generateList) {
|
||||
let html = generateList.build({
|
||||
list: JobsList
|
||||
});
|
||||
return html;
|
||||
},
|
||||
controller: 'JobsListController'
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
Dataset: ['JobsList', 'QuerySet', '$stateParams', 'GetBasePath',
|
||||
function(list, qs, $stateParams, GetBasePath) {
|
||||
let path = `${GetBasePath('instance_groups')}${$stateParams.instance_group_id}/jobs`;
|
||||
return qs.search(path, $stateParams[`${list.iterator}_search`]);
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
82
awx/ui/client/src/instance-groups/jobs/jobs.controller.js
Normal file
82
awx/ui/client/src/instance-groups/jobs/jobs.controller.js
Normal file
@ -0,0 +1,82 @@
|
||||
export default ['$scope','JobsList', 'GetBasePath', 'Rest', 'Dataset','Find', '$state', '$q',
|
||||
function($scope, JobsList, GetBasePath, Rest, Dataset, Find, $state, $q) {
|
||||
|
||||
let list = JobsList;
|
||||
|
||||
init();
|
||||
|
||||
function init(){
|
||||
$scope.optionsDefer = $q.defer();
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
}
|
||||
|
||||
$scope.$on(`${list.iterator}_options`, function(event, data){
|
||||
$scope.options = data.data.actions.GET;
|
||||
optionsRequestDataProcessing();
|
||||
});
|
||||
|
||||
// iterate over the list and add fields like type label, after the
|
||||
// OPTIONS request returns, or the list is sorted/paginated/searched
|
||||
function optionsRequestDataProcessing(){
|
||||
|
||||
if($scope[list.name] && $scope[list.name].length > 0) {
|
||||
$scope[list.name].forEach(function(item, item_idx) {
|
||||
var itm = $scope[list.name][item_idx];
|
||||
if(item.summary_fields && item.summary_fields.source_workflow_job &&
|
||||
item.summary_fields.source_workflow_job.id){
|
||||
item.workflow_result_link = `/#/workflows/${item.summary_fields.source_workflow_job.id}`;
|
||||
}
|
||||
|
||||
// Set the item type label
|
||||
if (list.fields.type && $scope.options &&
|
||||
$scope.options.hasOwnProperty('type')) {
|
||||
$scope.options.type.choices.forEach(function(choice) {
|
||||
if (choice[0] === item.type) {
|
||||
itm.type_label = choice[1];
|
||||
}
|
||||
});
|
||||
}
|
||||
buildTooltips(itm);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function buildTooltips(job) {
|
||||
job.status_tip = 'Job ' + job.status + ". Click for details.";
|
||||
}
|
||||
|
||||
$scope.viewjobResults = function(job) {
|
||||
var goTojobResults = function(state) {
|
||||
$state.go(state, { id: job.id }, { reload: true });
|
||||
};
|
||||
switch (job.type) {
|
||||
case 'job':
|
||||
goTojobResults('jobResult');
|
||||
break;
|
||||
case 'ad_hoc_command':
|
||||
goTojobResults('adHocJobStdout');
|
||||
break;
|
||||
case 'system_job':
|
||||
goTojobResults('managementJobStdout');
|
||||
break;
|
||||
case 'project_update':
|
||||
goTojobResults('scmUpdateStdout');
|
||||
break;
|
||||
case 'inventory_update':
|
||||
goTojobResults('inventorySyncStdout');
|
||||
break;
|
||||
case 'workflow_job':
|
||||
goTojobResults('workflowResults');
|
||||
break;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$scope.$watchCollection(`${$scope.list.name}`, function() {
|
||||
optionsRequestDataProcessing();
|
||||
}
|
||||
);
|
||||
}
|
||||
];
|
76
awx/ui/client/src/instance-groups/jobs/jobs.list.js
Normal file
76
awx/ui/client/src/instance-groups/jobs/jobs.list.js
Normal file
@ -0,0 +1,76 @@
|
||||
export default ['i18n', function (i18n) {
|
||||
return {
|
||||
name: 'jobs',
|
||||
iterator: 'job',
|
||||
basePath: 'api/v2/instance_groups/{{$stateParams.instance_group_id}}/jobs/',
|
||||
index: false,
|
||||
hover: false,
|
||||
well: true,
|
||||
emptyListText: i18n._('No jobs have yet run.'),
|
||||
listTitle: false,
|
||||
|
||||
fields: {
|
||||
status: {
|
||||
label: '',
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumn--smallStatus',
|
||||
dataTipWatch: 'job.status_tip',
|
||||
awToolTip: "{{ job.status_tip }}",
|
||||
awTipPlacement: "right",
|
||||
dataTitle: "{{ job.status_popover_title }}",
|
||||
icon: 'icon-job-{{ job.status }}',
|
||||
iconOnly: true,
|
||||
ngClick: "viewjobResults(job)",
|
||||
nosort: true
|
||||
},
|
||||
id: {
|
||||
label: i18n._('ID'),
|
||||
ngClick: "viewjobResults(job)",
|
||||
columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent',
|
||||
awToolTip: "{{ job.status_tip }}",
|
||||
dataPlacement: 'top',
|
||||
noLink: true
|
||||
},
|
||||
name: {
|
||||
label: i18n._('Name'),
|
||||
columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6',
|
||||
ngClick: "viewjobResults(job)",
|
||||
badgePlacement: 'right',
|
||||
badgeCustom: true,
|
||||
nosort: true,
|
||||
badgeIcon: `<a href="{{ job.workflow_result_link }}"
|
||||
aw-tool-tip="{{'View workflow results'|translate}}"
|
||||
data-placement="top"
|
||||
data-original-title="" title="">
|
||||
<i class="WorkflowBadge"
|
||||
ng-show="job.launch_type === 'workflow' ">
|
||||
W
|
||||
</i>
|
||||
</a>`
|
||||
},
|
||||
type: {
|
||||
label: i18n._('Type'),
|
||||
ngBind: 'job.type_label',
|
||||
columnClass: "col-lg-2 hidden-md hidden-sm hidden-xs",
|
||||
nosort: true
|
||||
},
|
||||
finished: {
|
||||
label: i18n._('Finished'),
|
||||
noLink: true,
|
||||
filter: "longDate",
|
||||
columnClass: "col-lg-2 col-md-3 col-sm-3 hidden-xs",
|
||||
key: true,
|
||||
desc: true,
|
||||
nosort: true
|
||||
},
|
||||
labels: {
|
||||
label: i18n._('Labels'),
|
||||
type: 'labels',
|
||||
nosort: true,
|
||||
showDelete: false,
|
||||
columnClass: 'List-tableCell col-lg-4 col-md-4 hidden-sm hidden-xs',
|
||||
sourceModel: 'labels',
|
||||
sourceField: 'name'
|
||||
},
|
||||
}
|
||||
};
|
||||
}];
|
@ -1,42 +1,19 @@
|
||||
export default ['$scope', 'InstanceGroupList', 'GetBasePath', 'Rest', 'Dataset','Find', '$state', '$q',
|
||||
function($scope, InstanceGroupList, GetBasePath, Rest, Dataset, Find, $state, $q) {
|
||||
export default ['$scope', 'InstanceGroupList', 'GetBasePath', 'Rest', 'Dataset','Find', '$state',
|
||||
function($scope, InstanceGroupList, GetBasePath, Rest, Dataset, Find, $state) {
|
||||
let list = InstanceGroupList;
|
||||
|
||||
init();
|
||||
|
||||
function init(){
|
||||
$scope.optionsDefer = $q.defer();
|
||||
$scope.list = list;
|
||||
$scope[`${list.iterator}_dataset`] = Dataset.data;
|
||||
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
|
||||
$scope.instanceGroupCount = Dataset.data.count;
|
||||
}
|
||||
|
||||
// iterate over the list and add fields like type label, after the
|
||||
// OPTIONS request returns, or the list is sorted/paginated/searched
|
||||
function optionsRequestDataProcessing(){
|
||||
$scope.optionsDefer.promise.then(function(options) {
|
||||
if($scope.list.name === 'instance_groups'){
|
||||
if ($scope[list.name] !== undefined) {
|
||||
$scope[list.name].forEach(function(item, item_idx) {
|
||||
var itm = $scope[list.name][item_idx];
|
||||
// Set the item type label
|
||||
if (list.fields.kind && options && options.actions && options.actions.GET && options.actions.GET.kind) {
|
||||
options.actions.GET.kind.choices.forEach(function(choice) {
|
||||
if (choice[0] === item.kind) {
|
||||
itm.kind_label = choice[1];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$scope.$watchCollection(`${$scope.list.name}`, function() {
|
||||
optionsRequestDataProcessing();
|
||||
}
|
||||
);
|
||||
$scope.isActive = function(id) {
|
||||
let selected = parseInt($state.params.instance_group_id);
|
||||
return id === selected;
|
||||
};
|
||||
}
|
||||
];
|
@ -0,0 +1,63 @@
|
||||
<div class="List-header">
|
||||
<div class="List-title">
|
||||
<div class="List-titleText" translate>
|
||||
INSTANCE GROUPS
|
||||
</div>
|
||||
<span class="badge List-titleBadge">
|
||||
{{ instanceGroupCount }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<smart-search django-model="instance_groups" base-path="instance_groups" iterator="instance_group" list="list" dataset="instance_group_dataset"
|
||||
collection="instance_groups" search-tags="searchTags">
|
||||
</smart-search>
|
||||
|
||||
<div class="List-noItems" ng-show="instance_groups.length < 1" translate>PLEASE ADD ITEMS TO THIS LIST</div>
|
||||
|
||||
<div class="list-table-container" ng-show="instance_groups.length > 0">
|
||||
<table id="instance_groups_table" class="List-table" is-extended="false">
|
||||
<thead>
|
||||
<tr class="List-tableHeaderRow">
|
||||
<th id="instance_group-name-header" class="List-tableHeader list-header col-md-5 col-sm-5 col-xs-5" ng-click="columnNoSort !== 'true' && toggleColumnOrderBy()"
|
||||
ng-class="{'list-header-noSort' : columnNoSort === 'true'}" base-path="instance_groups" collection="instance_groups"
|
||||
dataset="instance_group_dataset" column-sort="" column-field="name" column-iterator="instance_group" column-no-sort="undefined"
|
||||
column-label="Name" column-custom-class="col-md-5 col-sm-5 col-xs-5" query-set="instance_group_queryset">
|
||||
"{{'Name' | translate}}"
|
||||
<i ng-hide="columnNoSort === 'true'" class="fa columnSortIcon fa-sort-up" ng-class="orderByIcon()"></i>
|
||||
</th>
|
||||
<th id="instance_group-percent_capacity_remaining-header" class="List-tableHeader list-header list-header-noSort" translate>
|
||||
Capacity
|
||||
</th>
|
||||
<th id="instance_group-jobs_running-header" class="List-tableHeader list-header list-header-noSort" translate>
|
||||
Running Jobs
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- ngRepeat: instance_group in instance_groups -->
|
||||
<tr ng-class="{isActive: isActive(instance_group.id)}" id="instance_group.id" class="List-tableRow instance_group_class ng-scope" ng-repeat="instance_group in instance_groups">
|
||||
<td class="List-tableCell name-column col-md-5 col-sm-5 col-xs-5">
|
||||
<a ui-sref="instanceGroups.instances.list({instance_group_id: instance_group.id})" class="ng-binding" >{{ instance_group.name }}</a>
|
||||
<span class="badge List-titleBadge">{{ instance_group.instances }}</span>
|
||||
</td>
|
||||
<td class="List-tableCell List-tableCell--capacityRemainingColumn ng-binding">
|
||||
<capacity-bar capacity="instance_group.percent_capacity_remaining"></capacity-bar><span>{{ instance_group.percent_capacity_remaining }}%</span>
|
||||
</td>
|
||||
<td class="List-tableCell jobs_running-column ng-binding">
|
||||
<a ui-sref="instanceGroups.instances.jobs({instance_group_id: instance_group.id})">
|
||||
{{ instance_group.jobs_running }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<paginate
|
||||
base-path="instance_groups"
|
||||
iterator="instance_group"
|
||||
dataset="instance_group_dataset"
|
||||
collection="instance_groups"
|
||||
query-set="instance_group_queryset">
|
||||
</paginate>
|
@ -1,35 +1,58 @@
|
||||
import InstanceGroupsList from './list/instance-groups-list.controller';
|
||||
import instanceGroupsMultiselect from './instance-groups-multiselect/instance-groups.directive';
|
||||
import instanceGroupsModal from './instance-groups-multiselect/instance-groups-modal/instance-groups-modal.directive';
|
||||
import instanceGroupsMultiselect from '../shared/instance-groups-multiselect/instance-groups.directive';
|
||||
import instanceGroupsModal from '../shared/instance-groups-multiselect/instance-groups-modal/instance-groups-modal.directive';
|
||||
import instanceGroupsRoute from './instance-groups.route';
|
||||
import instancesListRoute from './instances/instances-list.route';
|
||||
import JobsList from './jobs/jobs.list';
|
||||
import jobsListRoute from './jobs/jobs-list.route';
|
||||
import JobsListController from './jobs/jobs.controller';
|
||||
import InstanceList from './instances/instances.list';
|
||||
import instancesRoute from './instances/instances.route';
|
||||
import InstanceListController from './instances/instances.controller';
|
||||
import InstanceJobsList from './instances/instance-jobs/instance-jobs.list';
|
||||
import instanceJobsRoute from './instances/instance-jobs/instance-jobs.route';
|
||||
import instanceJobsListRoute from './instances/instance-jobs/instance-jobs-list.route';
|
||||
import InstanceJobsController from './instances/instance-jobs/instance-jobs.controller';
|
||||
import CapacityBar from './capacity-bar/main';
|
||||
import list from './instance-groups.list';
|
||||
import service from './instance-groups.service';
|
||||
import { N_ } from '../i18n';
|
||||
|
||||
export default
|
||||
angular.module('instanceGroups', [])
|
||||
angular.module('instanceGroups', [CapacityBar.name])
|
||||
.service('InstanceGroupsService', service)
|
||||
.factory('InstanceGroupList', list)
|
||||
.factory('JobsList', JobsList)
|
||||
.factory('InstanceList', InstanceList)
|
||||
.factory('InstanceJobsList', InstanceJobsList)
|
||||
.controller('InstanceGroupsList', InstanceGroupsList)
|
||||
.controller('JobsListController', JobsListController)
|
||||
.controller('InstanceListController', InstanceListController)
|
||||
.controller('InstanceJobsController', InstanceJobsController)
|
||||
.directive('instanceGroupsMultiselect', instanceGroupsMultiselect)
|
||||
.directive('instanceGroupsModal', instanceGroupsModal)
|
||||
.config(['$stateProvider', 'stateDefinitionsProvider',
|
||||
function($stateProvider, stateDefinitionsProvider) {
|
||||
let stateDefinitions = stateDefinitionsProvider.$get();
|
||||
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
|
||||
function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
|
||||
let stateExtender = $stateExtenderProvider.$get();
|
||||
|
||||
|
||||
function generateInstanceGroupsStates() {
|
||||
return new Promise((resolve) => {
|
||||
resolve({
|
||||
states: [
|
||||
stateExtender.buildDefinition(instanceGroupsRoute),
|
||||
stateExtender.buildDefinition(instancesRoute),
|
||||
stateExtender.buildDefinition(instancesListRoute),
|
||||
stateExtender.buildDefinition(jobsListRoute),
|
||||
stateExtender.buildDefinition(instanceJobsRoute),
|
||||
stateExtender.buildDefinition(instanceJobsListRoute)
|
||||
]
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$stateProvider.state({
|
||||
name: 'instanceGroups',
|
||||
url: '/instance_groups',
|
||||
lazyLoad: () => stateDefinitions.generateTree({
|
||||
parent: 'instanceGroups',
|
||||
list: 'InstanceGroupList',
|
||||
controllers: {
|
||||
list: 'InstanceGroupsList'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
parent: 'setup',
|
||||
label: N_('INSTANCE GROUPS')
|
||||
}
|
||||
})
|
||||
lazyLoad: () => generateInstanceGroupsStates()
|
||||
});
|
||||
}
|
||||
]);
|
||||
}]);
|
||||
|
@ -382,6 +382,8 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
||||
html += "<a href=\"\" " + Attr(field, 'ngClick') + " ";
|
||||
} else if (field.ngHref) {
|
||||
html += "<a ng-href=\"" + field.ngHref + "\" ";
|
||||
} else if (field.uiSref) {
|
||||
html += "<a ui-sref=\"" + field.uiSref + "\" ";
|
||||
} else if (field.link || (field.key && (field.link === undefined || field.link))) {
|
||||
html += "<a href=\"#/" + base + "/{{" + list.iterator + ".id }}\" ";
|
||||
} else {
|
||||
|
@ -4,7 +4,7 @@ export default ['templateUrl', function(templateUrl) {
|
||||
scope: {
|
||||
instanceGroups: '='
|
||||
},
|
||||
templateUrl: templateUrl('instance-groups/instance-groups-multiselect/instance-groups-modal/instance-groups-modal'),
|
||||
templateUrl: templateUrl('shared/instance-groups-multiselect/instance-groups-modal/instance-groups-modal'),
|
||||
|
||||
link: function(scope, element) {
|
||||
|
||||
@ -47,7 +47,7 @@ export default ['templateUrl', function(templateUrl) {
|
||||
instanceGroupList.well = false;
|
||||
instanceGroupList.multiSelect = true;
|
||||
instanceGroupList.multiSelectExtended = true;
|
||||
delete instanceGroupList.fields.capacity;
|
||||
delete instanceGroupList.fields.percent_capacity_remaining;
|
||||
delete instanceGroupList.fields.jobs_running;
|
||||
|
||||
let html = `${GenerateList.build({
|
@ -2,8 +2,7 @@
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header Form-header">
|
||||
<div class="Form-title Form-title--uppercase">INSTANCE GROUPS</div>
|
||||
<!-- optional: transclude header fields -->
|
||||
<div class="Form-title Form-title--uppercase" translate>Select Instance Groups</div>
|
||||
<div class="Form-header--fields"></div>
|
||||
<div class="Form-exitHolder">
|
||||
<button type="button" class="Form-exit" ng-click="cancelForm()">
|
||||
@ -15,10 +14,9 @@
|
||||
<div id="instance-groups-modal-body"> {{ instance_group }} </div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" ng-click="cancelForm()" class="Lookup-cancel btn btn-default">CANCEL</button>
|
||||
<button type="button" ng-click="saveForm()" ng-disabled="!instance_groups || instance_groups.length === 0" class="Lookup-save btn btn-primary">SAVE</button>
|
||||
<button type="button" ng-click="cancelForm()" class="Lookup-cancel btn btn-default" translate>CANCEL</button>
|
||||
<button type="button" ng-click="saveForm()" ng-disabled="!instance_groups || instance_groups.length === 0" class="Lookup-save btn btn-primary" translate>SAVE</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,22 @@
|
||||
@import "../../shared/branding/colors.default.less";
|
||||
|
||||
#InstanceGroups {
|
||||
display: flex;
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
#instance-groups-panel {
|
||||
table {
|
||||
overflow: hidden;
|
||||
}
|
||||
.List-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.isActive {
|
||||
border-left: 10px solid @list-row-select-bord;
|
||||
}
|
||||
.instances-list,
|
||||
.instance-jobs-list {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ export default ['templateUrl', '$compile',
|
||||
instanceGroups: '='
|
||||
},
|
||||
restrict: 'E',
|
||||
templateUrl: templateUrl('instance-groups/instance-groups-multiselect/instance-groups'),
|
||||
templateUrl: templateUrl('shared/instance-groups-multiselect/instance-groups'),
|
||||
controller: instanceGroupsMultiselectController,
|
||||
link: function(scope) {
|
||||
scope.openInstanceGroupsModal = function() {
|
@ -1,6 +1,6 @@
|
||||
<div class="input-group Form-mixedInputGroup">
|
||||
<span class="input-group-btn Form-variableHeightButtonGroup">
|
||||
<button type="button" class="Form-lookupButton Form-lookupButton--variableHeight btn btn-default" ng-click="openInstanceGroupsModal()">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" class="Form-lookupButton btn btn-default" ng-click="openInstanceGroupsModal()">
|
||||
<i class="fa fa-search"></i>
|
||||
</button>
|
||||
</span>
|
Loading…
Reference in New Issue
Block a user