adding inventories related-host state

This commit is contained in:
jaredevantabor 2017-04-12 17:20:19 -07:00 committed by Jared Tabor
parent b85614082e
commit 09f99b04f2
13 changed files with 654 additions and 33 deletions

View File

@ -10,8 +10,10 @@
* @description This form is for adding/editing an inventory
*/
export default ['i18n', 'buildGroupsListState', 'buildGroupsAddState', 'buildGroupsEditState',
function(i18n, buildGroupsListState, buildGroupsAddState, buildGroupsEditState) {
export default ['i18n', 'buildGroupsListState', 'buildGroupsAddState',
'buildGroupsEditState', 'buildHostListState',
function(i18n, buildGroupsListState, buildGroupsAddState, buildGroupsEditState,
buildHostListState) {
return {
addTitle: i18n._('NEW INVENTORY'),
@ -143,35 +145,12 @@ function(i18n, buildGroupsListState, buildGroupsAddState, buildGroupsEditState)
},
hosts: {
name: 'hosts',
// awToolTip: i18n._('Please save before assigning permissions'),
// dataPlacement: 'top',
basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/hosts/',
type: 'collection',
include: "RelatedHostsListDefinition",
title: i18n._('Hosts'),
iterator: 'host',
index: false,
open: false,
// search: {
// order_by: 'username'
// },
actions: {
add: {
label: i18n._('Add'),
ngClick: "$state.go('.add')",
awToolTip: i18n._('Add a permission'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '+ ADD',
// ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
fields: {
name: {
label: i18n._('Name'),
// linkBase: 'users',
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
}
}
listState: buildHostListState,
// addState: buildGroupsAddState,
// editState: buildGroupsEditState
},
inventory_sources: {
name: 'inventory_sources',

View File

@ -6,6 +6,7 @@
import host from './hosts/main';
import group from './groups/main';
import relatedHost from './related-hosts/main';
import inventoryAdd from './add/main';
import inventoryEdit from './edit/main';
import inventoryList from './list/main';
@ -18,6 +19,7 @@ export default
angular.module('inventory', [
host.name,
group.name,
relatedHost.name,
inventoryAdd.name,
inventoryEdit.name,
inventoryList.name
@ -116,10 +118,7 @@ angular.module('inventory', [
controllers: {
list: 'InventoryListController',
add: 'InventoryAddController',
edit: 'InventoryEditController',
related: {
groups: 'GroupsListController'
}
edit: 'InventoryEditController'
},
urls: {
list: '/inventories'

View File

@ -0,0 +1,63 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
export default ['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange',
'GenerateForm', 'HostManageService', 'rbacUiControlService', 'GetBasePath', 'ToJSON',
function($state, $stateParams, $scope, HostForm, ParseTypeChange,
GenerateForm, HostManageService, rbacUiControlService, GetBasePath, ToJSON) {
init();
function init() {
$scope.canAdd = false;
rbacUiControlService.canAdd(GetBasePath('inventory') + $stateParams.inventory_id + "/hosts")
.then(function(canAdd) {
$scope.canAdd = canAdd;
});
$scope.parseType = 'yaml';
$scope.host = { enabled: true };
// apply form definition's default field values
GenerateForm.applyDefaults(HostForm, $scope);
ParseTypeChange({
scope: $scope,
field_id: 'host_variables',
variable: 'variables',
parse_variable: 'parseType'
});
}
$scope.formCancel = function() {
$state.go('^');
};
$scope.toggleHostEnabled = function() {
if ($scope.host.has_inventory_sources){
return;
}
$scope.host.enabled = !$scope.host.enabled;
};
$scope.formSave = function(){
var json_data = ToJSON($scope.parseType, $scope.variables, true),
params = {
variables: json_data,// $scope.variables === '---' || $scope.variables === '{}' ? null : $scope.variables,
name: $scope.name,
description: $scope.description,
enabled: $scope.host.enabled,
inventory: $stateParams.inventory_id
};
HostManageService.post(params).then(function(res) {
// assign the host to current group if not at the root level
if ($stateParams.group) {
HostManageService.associateGroup(res.data, _.last($stateParams.group)).then(function() {
$state.go('inventoryManage.editHost', { host_id: res.data.id }, { reload: true });
});
} else {
$state.go('inventoryManage.editHost', { host_id: res.data.id }, { reload: true });
}
});
};
}
];

View File

@ -0,0 +1,11 @@
/*************************************************
* Copyright (c) 2017 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import controller from './host-add.controller';
export default
angular.module('relatedHostsAdd', [])
.controller('RelatedHostAddController', controller);

View File

@ -0,0 +1,79 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
export default
['$scope', '$state', '$stateParams', 'DashboardHostsForm', 'GenerateForm', 'ParseTypeChange', 'DashboardHostService', 'host',
function($scope, $state, $stateParams, DashboardHostsForm, GenerateForm, ParseTypeChange, DashboardHostService, host){
$scope.parseType = 'yaml';
$scope.formCancel = function(){
$state.go('^', null, {reload: true});
};
$scope.toggleHostEnabled = function(){
if ($scope.host.has_inventory_sources){
return;
}
$scope.host.enabled = !$scope.host.enabled;
};
$scope.toggleEnabled = function(){
$scope.host.enabled = !$scope.host.enabled;
};
$scope.formSave = function(){
var host = {
id: $scope.host.id,
variables: $scope.variables === '---' || $scope.variables === '{}' ? null : $scope.variables,
name: $scope.name,
description: $scope.description,
enabled: $scope.host.enabled
};
DashboardHostService.putHost(host).then(function(){
$state.go('^', null, {reload: true});
});
};
var init = function(){
$scope.host = host.data;
$scope.name = host.data.name;
$scope.description = host.data.description;
$scope.variables = getVars(host.data.variables);
ParseTypeChange({
scope: $scope,
field_id: 'host_variables',
variable: 'variables',
});
};
// Adding this function b/c sometimes extra vars are returned to the
// UI as a string (ex: "foo: bar"), and other times as a
// json-object-string (ex: "{"foo": "bar"}"). CodeMirror wouldn't know
// how to prettify the latter. The latter occurs when host vars were
// system generated and not user-input (such as adding a cloud host);
function getVars(str){
// Quick function to test if the host vars are a json-object-string,
// by testing if they can be converted to a JSON object w/o error.
function IsJsonString(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
if(str === ''){
return '---';
}
else if(IsJsonString(str)){
str = JSON.parse(str);
return jsyaml.safeDump(str);
}
else if(!IsJsonString(str)){
return str;
}
}
init();
}];

View File

@ -0,0 +1,11 @@
/*************************************************
* Copyright (c) 2017 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import controller from './host-edit.controller';
export default
angular.module('relatedHostEdit', [])
.controller('HostEditController', controller);

View File

@ -0,0 +1,89 @@
/*************************************************
* Copyright (c) 2017 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import RelatedHostListController from './host-list.controller';
export default ['RelatedHostsListDefinition', '$stateExtender', 'templateUrl', '$injector',
function(RelatedHostsListDefinition, $stateExtender, templateUrl, $injector){
var val = function(field, formStateDefinition) {
let state,
list = field.include ? $injector.get(field.include) : field,
breadcrumbLabel = (field.iterator.replace('_', ' ') + 's').toUpperCase(),
stateConfig = {
searchPrefix: `${list.iterator}`,
name: `${formStateDefinition.name}.${list.iterator}s`,
url: `/${list.iterator}s`,
ncyBreadcrumb: {
parent: `${formStateDefinition.name}`,
label: `${breadcrumbLabel}`
},
params: {
[list.iterator + '_search']: {
value: { order_by: field.order_by ? field.order_by : 'name' }
},
},
views: {
'related': {
templateProvider: function(RelatedHostsListDefinition, generateList, $stateParams, GetBasePath) {
let list = _.cloneDeep(RelatedHostsListDefinition);
if($stateParams && $stateParams.group) {
list.basePath = GetBasePath('groups') + _.last($stateParams.group) + '/all_hosts';
}
else {
//reaches here if the user is on the root level group
list.basePath = GetBasePath('inventory') + $stateParams.inventory_id + '/hosts';
}
let html = generateList.build({
list: list,
mode: 'edit'
});
return html;
},
controller: RelatedHostListController
}
},
resolve: {
ListDefinition: () => {
return list;
},
Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath', '$interpolate', '$rootScope',
(list, qs, $stateParams, GetBasePath, $interpolate, $rootScope) => {
// allow related list definitions to use interpolated $rootScope / $stateParams in basePath field
let path, interpolator;
if (GetBasePath(list.basePath)) {
path = GetBasePath(list.basePath);
} else {
interpolator = $interpolate(list.basePath);
path = interpolator({ $rootScope: $rootScope, $stateParams: $stateParams });
}
return qs.search(path, $stateParams[`${list.iterator}_search`]);
}
],
hostsUrl: ['InventoryManageService', '$stateParams', function(InventoryManageService, $stateParams) {
return $stateParams.group && $stateParams.group.length > 0 ?
// nested context - provide all hosts managed by nodes
InventoryManageService.childHostsUrl(_.last($stateParams.group)) :
// root context - provide all hosts in an inventory
InventoryManageService.rootHostsUrl($stateParams.inventory_id);
}],
hostsDataset: ['RelatedHostsListDefinition', 'QuerySet', '$stateParams', 'hostsUrl', (list, qs, $stateParams, hostsUrl) => {
let path = hostsUrl;
return qs.search(path, $stateParams[`${list.iterator}_search`]);
}],
inventoryData: ['InventoryManageService', '$stateParams', function(InventoryManageService, $stateParams) {
return InventoryManageService.getInventory($stateParams.inventory_id).then(res => res.data);
}]
}
};
state = $stateExtender.buildDefinition(stateConfig);
// appy any default search parameters in form definition
if (field.search) {
state.params[`${field.iterator}_search`].value = _.merge(state.params[`${field.iterator}_search`].value, field.search);
}
return state;
};
return val;
}
];

View File

@ -0,0 +1,123 @@
/*************************************************
* Copyright (c) 2017 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
// import HostManageService from './../hosts/host.service';
export default ['$scope', 'RelatedHostsListDefinition', '$rootScope', 'GetBasePath',
'rbacUiControlService', 'Dataset', '$state', '$filter', 'Prompt', 'Wait',
'HostManageService', 'SetStatus',
function($scope, RelatedHostsListDefinition, $rootScope, GetBasePath,
rbacUiControlService, Dataset, $state, $filter, Prompt, Wait,
HostManageService, SetStatus) {
let list = RelatedHostsListDefinition;
init();
function init(){
$scope.canAdd = false;
$scope.enableSmartInventoryButton = false;
rbacUiControlService.canAdd('hosts')
.then(function(canAdd) {
$scope.canAdd = canAdd;
});
// Search init
$scope.list = list;
$scope[`${list.iterator}_dataset`] = Dataset.data;
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
$rootScope.flashMessage = null;
$scope.$watchCollection(list.name, function() {
$scope[list.name] = _.map($scope.hosts, function(value) {
value.inventory_name = value.summary_fields.inventory.name;
value.inventory_id = value.summary_fields.inventory.id;
return value;
});
setJobStatus();
});
$rootScope.$on('$stateChangeSuccess', function(event, toState, toParams) {
if(toState.name === 'hosts.addSmartInventory') {
$scope.enableSmartInventoryButton = false;
}
else {
if(toParams && toParams.host_search) {
let hasMoreThanDefaultKeys = false;
angular.forEach(toParams.host_search, function(value, key) {
if(key !== 'order_by' && key !== 'page_size') {
hasMoreThanDefaultKeys = true;
}
});
$scope.enableSmartInventoryButton = hasMoreThanDefaultKeys ? true : false;
}
else {
$scope.enableSmartInventoryButton = false;
}
}
});
}
function setJobStatus(){
_.forEach($scope.hosts, function(value) {
SetStatus({
scope: $scope,
host: value
});
});
}
$scope.createHost = function(){
$state.go('inventories.edit.hosts.add');
};
$scope.editHost = function(id){
$state.go('inventories.edit.hosts.edit', {host_id: id});
};
$scope.deleteHost = function(id, name){
var body = '<div class=\"Prompt-bodyQuery\">Are you sure you want to permanently delete the host below from the inventory?</div><div class=\"Prompt-bodyTarget\">' + $filter('sanitize')(name) + '</div>';
var action = function(){
delete $rootScope.promptActionBtnClass;
Wait('start');
HostManageService.delete(id).then(() => {
$('#prompt-modal').modal('hide');
if (parseInt($state.params.host_id) === id) {
$state.go("hosts", null, {reload: true});
} else {
$state.go($state.current.name, null, {reload: true});
}
Wait('stop');
});
};
// Prompt depends on having $rootScope.promptActionBtnClass available...
Prompt({
hdr: 'Delete Host',
body: body,
action: action,
actionText: 'DELETE',
});
$rootScope.promptActionBtnClass = 'Modal-errorButton';
};
$scope.toggleHost = function(event, host) {
try {
$(event.target).tooltip('hide');
} catch (e) {
// ignore
}
host.enabled = !host.enabled;
HostManageService.put(host).then(function(){
$state.go($state.current, null, {reload: true});
});
};
$scope.smartInventory = function() {
$state.go('inventories.addSmartInventory');
};
}];

View File

@ -0,0 +1,13 @@
/*************************************************
* Copyright (c) 2017 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import buildHostListState from './build-host-list-state.factory';
import RelatedHostListController from './host-list.controller';
export default
angular.module('relatedHostList', [])
.factory('buildHostListState', buildHostListState)
.controller('RelatedHostListController', RelatedHostListController);

View File

@ -0,0 +1,27 @@
/*************************************************
* Copyright (c) 2017 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
import relatedHostAdd from './add/main';
import relatedHostEdit from './edit/main';
import relatedHostList from './list/main';
import relatedHostsListDefinition from './related-host.list';
import relatedHostsFormDefinition from './related-host.form';
// import HostManageService from './hosts.service';
// import SetStatus from './set-status.factory';
// import SetEnabledMsg from './set-enabled-msg.factory';
// import SmartInventory from './smart-inventory/main';
export default
angular.module('relatedHost', [
relatedHostAdd.name,
relatedHostEdit.name,
relatedHostList.name
])
.value('RelatedHostsFormDefinition', relatedHostsFormDefinition)
.value('RelatedHostsListDefinition', relatedHostsListDefinition);
// .factory('SetStatus', SetStatus)
// .factory('SetEnabledMsg', SetEnabledMsg)
// .service('HostManageService', HostManageService);

View File

@ -0,0 +1,103 @@
/*************************************************
* Copyright (c) 2015 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
/**
* @ngdoc function
* @name forms.function:Hosts
* @description This form is for adding/editing a host on the inventory page
*/
export default ['i18n', function(i18n) {
return {
addTitle: i18n._('CREATE HOST'),
editTitle: '{{ host.name }}',
name: 'host',
basePath: 'hosts',
well: false,
formLabelSize: 'col-lg-3',
formFieldSize: 'col-lg-9',
iterator: 'host',
headerFields:{
enabled: {
class: 'Form-header-field',
ngClick: 'toggleHostEnabled(host)',
type: 'toggle',
awToolTip: "<p>" +
i18n._("Indicates if a host is available and should be included in running jobs.") +
"</p><p>" +
i18n._("For hosts that are part of an external" +
" inventory, this flag cannot be changed. It will be" +
" set by the inventory sync process.") +
"</p>",
dataTitle: i18n._('Host Enabled'),
ngDisabled: 'host.has_inventory_sources'
}
},
fields: {
name: {
label: i18n._('Host Name'),
type: 'text',
required: true,
awPopOver: "<p>" +
i18n._("Provide a host name, ip address, or ip address:port. Examples include:") +
"</p>" +
"<blockquote>myserver.domain.com<br/>" +
"127.0.0.1<br />" +
"10.1.0.140:25<br />" +
"server.example.com:25" +
"</blockquote>",
dataTitle: i18n._('Host Name'),
dataPlacement: 'right',
dataContainer: 'body',
ngDisabled: '!(host.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
label: i18n._('Description'),
ngDisabled: '!(host.summary_fields.user_capabilities.edit || canAdd)',
type: 'text'
},
variables: {
label: i18n._('Variables'),
type: 'textarea',
rows: 6,
class: 'Form-formGroup--fullWidth',
"default": "---",
awPopOver: "<p>" + i18n._("Enter inventory variables using either JSON or YAML syntax. Use the radio button to toggle between the two.") + "</p>" +
"JSON:<br />\n" +
"<blockquote>{<br />&emsp;\"somevar\": \"somevalue\",<br />&emsp;\"password\": \"magic\"<br /> }</blockquote>\n" +
"YAML:<br />\n" +
"<blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>\n" +
'<p>' + i18n.sprintf(i18n._('View JSON examples at %s'), '<a href="http://www.json.org" target="_blank">www.json.org</a>') + '</p>' +
'<p>' + i18n.sprintf(i18n._('View YAML examples at %s'), '<a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a>') + '</p>',
dataTitle: i18n._('Host Variables'),
dataPlacement: 'right',
dataContainer: 'body'
},
inventory: {
type: 'hidden',
includeOnEdit: true,
includeOnAdd: true
}
},
buttons: {
cancel: {
ngClick: 'formCancel()',
ngShow: '(host.summary_fields.user_capabilities.edit || canAdd)'
},
close: {
ngClick: 'formCancel()',
ngShow: '!(host.summary_fields.user_capabilities.edit || canAdd)'
},
save: {
ngClick: 'formSave()',
ngDisabled: true,
ngShow: '(host.summary_fields.user_capabilities.edit || canAdd)'
}
},
};
}];

View File

@ -0,0 +1,118 @@
/*************************************************
* Copyright (c) 2017 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
export default {
name: 'hosts',
iterator: 'host',
editTitle: '{{ selected_group }}',
showTitle: false,
well: true,
wellOverride: true,
index: false,
hover: true,
// hasChildren: true,
multiSelect: true,
trackBy: 'host.id',
basePath: 'api/v2/inventories/{{$stateParams.inventory_id}}/hosts/',
fields: {
active_failures: {
label: '',
iconOnly: true,
nosort: true,
// do not remove this ng-click directive
// the list generator case to handle fields without ng-click
// cannot handle the aw-* directives
ngClick: 'noop()',
awPopOver: "{{ host.job_status_html }}",
dataTitle: "{{ host.job_status_title }}",
awToolTip: "{{ host.badgeToolTip }}",
dataPlacement: 'top',
icon: "{{ 'fa icon-job-' + host.active_failures }}",
id: 'active-failures-action',
columnClass: 'status-column List-staticColumn--smallStatus'
},
name: {
key: true,
label: 'Hosts',
ngClick: "editHost(host.id)",
ngClass: "{ 'host-disabled-label': !host.enabled }",
columnClass: 'col-lg-6 col-md-8 col-sm-8 col-xs-7',
dataHostId: "{{ host.id }}",
dataType: "host",
class: 'InventoryManage-breakWord'
}
},
fieldActions: {
columnClass: 'col-lg-6 col-md-4 col-sm-4 col-xs-5 text-right',
copy: {
mode: 'all',
ngClick: "copyMoveHost(host.id)",
awToolTip: 'Copy or move host to another group',
dataPlacement: "top",
ngShow: 'host.summary_fields.user_capabilities.edit'
},
edit: {
//label: 'Edit',
ngClick: "editHost(host.id)",
icon: 'icon-edit',
awToolTip: 'Edit host',
dataPlacement: 'top',
ngShow: 'host.summary_fields.user_capabilities.edit'
},
view: {
//label: 'Edit',
ngClick: "editHost(host.id)",
awToolTip: 'View host',
dataPlacement: 'top',
ngShow: '!host.summary_fields.user_capabilities.edit'
},
"delete": {
//label: 'Delete',
ngClick: "deleteHost(host.id, host.name)",
icon: 'icon-trash',
awToolTip: 'Delete host',
dataPlacement: 'top',
ngShow: 'host.summary_fields.user_capabilities.delete'
}
},
actions: {
system_tracking: {
buttonContent: 'System Tracking',
ngClick: 'systemTracking()',
awToolTip: "Select one or two hosts by clicking the checkbox beside the host. System tracking offers the ability to compare the results of two scan runs from different dates on one host or the same date on two hosts.",
dataTipWatch: "systemTrackingTooltip",
dataPlacement: 'top',
awFeature: 'system_tracking',
actionClass: 'btn List-buttonDefault system-tracking',
ngDisabled: 'systemTrackingDisabled || !hostsSelected',
showTipWhenDisabled: true,
tooltipInnerClass: "Tooltip-wide",
ngShow: true
},
refresh: {
mode: 'all',
awToolTip: "Refresh the page",
ngClick: "refreshGroups()",
ngShow: "socketStatus == 'error'",
actionClass: 'btn List-buttonDefault',
buttonContent: 'REFRESH'
},
create: {
mode: 'all',
ngClick: "createHost()",
awToolTip: "Create a new host",
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD HOST',
ngShow: 'canAdd',
dataPlacement: "top",
}
}
};

View File

@ -559,6 +559,12 @@ function($injector, $stateExtender, $log, i18n) {
states.push(field.editState(field, formStateDefinition, params));
states = _.flatten(states);
}
else if(field.iterator === 'host'){
states.push(field.listState(field, formStateDefinition));
}
// if(field && field.listState){
// states.push(field.listState(field, formStateDefinition));
// }
else if(field.iterator === 'notification'){
states.push(buildNotificationState(field));
states = _.flatten(states);