/********************************************* * Copyright (c) 2014 AnsibleWorks, Inc. * * GroupsHelper * * Routines that handle group add/edit/delete on the Inventory tree widget. * */ 'use strict'; angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'GroupListDefinition', 'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService', 'GroupsHelper', 'InventoryHelper', 'SelectionHelper', 'JobSubmissionHelper', 'RefreshHelper', 'PromptDialog', 'InventorySummaryHelpDefinition', 'CredentialsListDefinition', 'InventoryTree' ]) .factory('GetSourceTypeOptions', [ 'Rest', 'ProcessErrors', 'GetBasePath', function(Rest, ProcessErrors, GetBasePath) { return function(params) { // Lookup options for source and build an array of drop-down choices var scope = params.scope; var variable = params.variable; if (scope[variable] == undefined) { scope[variable] = []; Rest.setUrl(GetBasePath('inventory_sources')); Rest.options() .success( function(data, status, headers, config) { var choices = data.actions.GET.source.choices for (var i=0; i < choices.length; i++) { if (choices[i][0] !== 'file') { scope[variable].push({ label: (choices[i][0] == "") ? 'Manual' : choices[i][1] , value: choices[i][0] }); } } }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve options for inventory_sources.source. OPTIONS status: ' + status }); }); } } }]) .factory('GetUpdateIntervalOptions', [ function() { return function() { return [ { label: 'none', value: 0 }, { label: '5 minutes', value: 5 }, { label: '10 minutes', value: 10 }, { label: '15 minutes', value: 15 }, { label: '30 minutes', value: 30 }, { label: '45 minutes', value: 45 }, { label: '1 hour', value: 60 }, { label: '2 hours', value: 120 }, { label: '3 hours', value: 180 }, { label: '4 hours', value: 240 }, { label: '5 hours', value: 300 }, { label: '6 hours', value: 360 }, { label: '7 hours', value: 420 }, { label: '8 hours', value: 480 }, { label: '9 hours', value: 540 }, { label: '10 hours', value: 600 }, { label: '11 hours', value: 660 }, { label: '12 hours', value: 720 }, { label: '13 hours', value: 780 }, { label: '14 hours', value: 840 }, { label: '15 hours', value: 900 }, { label: '16 hours', value: 960 }, { label: '17 hours', value: 1020 }, { label: '18 hours', value: 1080 }, { label: '19 hours', value: 1140}, { label: '20 hours', value: 1200 }, { label: '21 hours', value: 1260 }, { label: '22 hours', value: 1320 }, { label: '23 hours', value: 1380 }, { label: '24 hours', value: 1440 }, { label: '48 hours', value: 1880 }, { label: '72 hours', value: 4320 }, { label: 'weekly (every 7 days)', value: 10080 }, { label: 'monthly (every 30 days)', value: 43200 } ]; } }]) .factory('ViewUpdateStatus', [ 'Rest', 'ProcessErrors', 'GetBasePath', 'ShowUpdateStatus', 'Alert', 'Wait', 'Empty', 'Find', function(Rest, ProcessErrors, GetBasePath, ShowUpdateStatus, Alert, Wait, Empty, Find) { return function(params) { var scope = params.scope; var tree_id = params.tree_id; var group_id = params.group_id; var found = false; var group = Find({ list: scope.groups, key: 'id', val: tree_id }); if (group) { if (Empty(group.source)) { Alert('Missing Configuration', 'The selected group is not configured for inventory sync. ' + 'You must first edit the group, provide Source settings, and then run the sync process.', 'alert-info'); } else if (Empty(group.status) || group.status == "never updated") { Alert('No Status Available', 'An inventory sync has not been performed for the selected group. Start the process by ' + 'clicking the button.', 'alert-info'); } else { Wait('start'); Rest.setUrl(group.related.inventory_source); Rest.get() .success( function(data, status, headers, config) { var url = (data.related.current_update) ? data.related.current_update : data.related.last_update; ShowUpdateStatus({ group_name: data.summary_fields.group.name, last_update: url, license_error: ( (data.summary_fields.last_update && data.summary_fields.last_update.license_error) ? true : false ), tree_id: tree_id, group_id: group_id, parent_scope: scope }); }) .error( function(data, status, headers, config) { Wait('stop'); ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to retrieve inventory source: ' + group.related.inventory_source + ' POST returned status: ' + status }); }); } } } }]) .factory('GetHostsStatusMsg', [ function() { return function(params) { var active_failures = params.active_failures; var total_hosts = params.total_hosts; var inventory_id = params.inventory_id; var group_id = params.group_id; var tip, failures, html_class; // Return values for use on host status indicator if (active_failures > 0) { tip = total_hosts + ( (total_hosts == 1) ? ' host' : ' hosts' ) + '. ' + active_failures + ' with failed jobs.'; html_class = 'true'; failures = true; } else { failures = false; if (total_hosts == 0) { // no hosts tip = "Group contains 0 hosts."; html_class = 'na'; } else { // many hosts with 0 failures tip = total_hosts + ( (total_hosts == 1) ? ' host' : ' hosts' ) + '. No job failures'; html_class = 'false'; } } return { tooltip: tip, failures: failures, 'class': html_class }; } }]) .factory('GetSyncStatusMsg', [ function() { return function(params) { var status = params.status; var launch_class = ''; var launch_tip = 'Start sync process'; var stat, stat_class, status_tip; stat = status; stat_class = 'icon-cloud-' + stat; switch (status) { case 'never updated': stat = 'never'; stat_class = 'icon-cloud-na disabled'; status_tip = 'Sync not performed. Click to start it now.'; break; case 'none': case '': launch_class = 'btn-disabled', stat = 'n/a'; stat_class = 'icon-cloud-na disabled'; status_tip = 'Cloud source not configured. Click to update.'; launch_tip = status_tip; break; case 'failed': status_tip = 'Sync failed. Click to view log.'; break; case 'successful': status_tip = 'Sync completed. Click to view log.'; break; case 'updating': status_tip = 'Sync running'; break; } return { 'class': stat_class, tooltip: status_tip, status: stat, 'launch_class': launch_class, 'launch_tip': launch_tip } } }]) .factory('SourceChange', [ 'GetBasePath', 'CredentialList', 'LookUpInit', function(GetBasePath, CredentialList, LookUpInit){ return function(params) { var scope = params.scope; var form = params.form; if (scope['source'].value == 'file') { scope.sourcePathRequired = true; } else { scope.sourcePathRequired = false; // reset fields scope.source_path = ''; scope[form.name + '_form']['source_path'].$setValidity('required',true); } if (scope['source'].value == 'rax') { scope['source_region_choices'] = scope['rax_regions']; //$('#s2id_group_source_regions').select2('data', []); $('#s2id_group_source_regions').select2('data', [{ id: 'all', text: 'All' }]); } else if (scope['source'].value == 'ec2') { scope['source_region_choices'] = scope['ec2_regions']; //$('#s2id_group_source_regions').select2('data', []); $('#s2id_group_source_regions').select2('data', [{ id: 'all', text: 'All' }]); } var kind = (scope.source.value == 'rax') ? 'rax' : 'aws'; var url = GetBasePath('credentials') + '?cloud=true&kind=' + kind; LookUpInit({ url: url, scope: scope, form: form, list: CredentialList, field: 'credential' }); } }]) // Cancel a pending or running inventory sync .factory('GroupsCancelUpdate', ['Rest', 'ProcessErrors', 'Alert', 'Wait', 'Find', function(Rest, ProcessErrors, Alert, Wait, Find) { return function(params) { var scope = params.scope; var id = params.tree_id; if (scope.removeCancelUpdate) { scope.removeCancelUpdate(); } scope.removeCancelUpdate = scope.$on('CancelUpdate', function(e, url) { // Cancel the update process Rest.setUrl(url) Rest.post() .success( function(data, status, headers, config) { Wait('stop'); Alert('Inventory Sync Cancelled', 'Your request to cancel the sync process was submitted to the task manger. ' + 'Click the button to monitor the status.', 'alert-info'); }) .error( function(data, status, headers, config) { Wait('stop'); ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST status: ' + status }); }); }); if (scope.removeCheckCancel) { scope.removeCheckCancel(); } scope.removeCheckCancel = scope.$on('CheckCancel', function(e, last_update, current_update) { // Check that we have access to cancelling an update var url = (current_update) ? current_update : last_update; url += 'cancel/'; Rest.setUrl(url); Rest.get() .success( function(data, status, headers, config) { if (data.can_cancel) { scope.$emit('CancelUpdate', url); } else { Wait('stop'); Alert('Cancel Inventory Sync', 'Either you do not have access or the sync process completed.
' + 'Click the button to view the latest status.', 'alert-info'); } }) .error( function(data, status, headers, config) { Wait('stop'); ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + ' failed. GET status: ' + status }); }); }); // Cancel the update process var group = Find({ list: scope.groups, key: 'id', val: id }); scope.selected_tree_id = group.id; scope.selected_group_id = group.group_id; if (group && (group.status == 'updating' || group.status == 'pending')) { // We found the group, and there is a running update Wait('start'); Rest.setUrl(group.related.inventory_source); Rest.get() .success( function(data, status, headers, config) { scope.$emit('CheckCancel', data.related.last_update, data.related.current_update); }) .error( function(data, status, headers, config) { Wait('stop'); ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + group.related.inventory_source + ' failed. GET status: ' + status }); }); } else { Alert('Cancel Inventory Sync', 'The sync process completed. Click the to' + ' view the latest status.', 'alert-info'); } } }]) .factory('GroupsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', 'Prompt', 'ProcessErrors', 'GetBasePath', 'ParseTypeChange', 'GroupsEdit', 'Wait', 'GetChoices', 'GetSourceTypeOptions', 'LookUpInit', 'BuildTree', 'SourceChange', function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, GetBasePath, ParseTypeChange, GroupsEdit, Wait, GetChoices, GetSourceTypeOptions, LookUpInit, BuildTree, SourceChange) { return function(params) { var inventory_id = params.inventory_id; var group_id = (params.group_id !== undefined) ? params.group_id : null; var parent_scope = params.scope; // Inject dynamic view var defaultUrl = (group_id !== null) ? GetBasePath('groups') + group_id + '/children/' : GetBasePath('inventory') + inventory_id + '/groups/'; var form = GroupForm; var generator = GenerateForm; var scope = generator.inject(form, { mode: 'add', modal: true, related: false, show_modal: false }); var groupCreated = false; $('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success'); scope.formModalActionLabel = 'Save'; scope.formModalCancelShow = true; scope.parseType = 'yaml'; scope.source = null; ParseTypeChange(scope); generator.reset(); var master={}; if (scope.removeAddTreeRefreshed) { scope.removeAddTreeRefreshed(); } scope.removeAddTreeRefreshed = scope.$on('GroupTreeRefreshed', function(e) { $rootScope.formModalHeader = null; $rootScope.formModalCancleShow= null; $rootScope.formModalActionLabel = null; Wait('stop'); $('#form-modal').modal('hide'); scope.removeAddTreeRefreshed(); }); if (scope.removeSaveComplete) { scope.removeSaveComplete(); } scope.removeSaveComplete = scope.$on('SaveComplete', function(e, group_id, error) { if (!error) { if (scope.searchCleanup) scope.searchCleanup(); scope.formModalActionDisabled = false; scope.showGroupHelp = false; //get rid of the Hint BuildTree({ scope: parent_scope, inventory_id: inventory_id, refresh: true, new_group_id: group_id }); } }); if (scope.removeFormSaveSuccess) { scope.removeFormSaveSuccess(); } scope.removeFormSaveSuccess = scope.$on('formSaveSuccess', function(e, group_id, url) { // Source data gets stored separately from the group. Validate and store Source // related fields, then call SaveComplete to wrap things up. var parseError = false; var saveError = false; // Update the selector tree with new group name, descr //SetNodeName({ scope: scope['selectedNode'], group_id: group_id, // name: scope.name, description: scope.description }); if (scope.source.value !== null && scope.source.value !== '') { var data = { group: group_id, source: scope['source'].value, source_path: scope['source_path'], credential: scope['credential'], overwrite: scope['overwrite'], overwrite_vars: scope['overwrite_vars'], update_on_launch: scope['update_on_launch'] //update_interval: scope['update_interval'].value }; // Create a string out of selected list of regions var regions = $('#s2id_group_source_regions').select2("data"); var r = []; for (var i=0; i < regions.length; i++) { r.push(regions[i].id); } data['source_regions'] = r.join(); if (scope['source'].value == 'ec2') { // for ec2, validate variable data try { if (scope.envParseType == 'json') { var json_data = JSON.parse(scope.source_vars); //make sure JSON parses } else { var json_data = jsyaml.load(scope.source_vars); //parse yaml } // Make sure our JSON is actually an object if (typeof json_data !== 'object') { throw "failed to return an object!"; } // Send JSON as a string if ($.isEmptyObject(json_data)) { data.source_vars = ""; } else { data.source_vars = JSON.stringify(json_data, undefined, '\t'); } } catch(err) { parseError = true; scope.$emit('SaveComplete', true); Alert("Error", "Error parsing extra variables. Parser returned: " + err); } } if (!parseError) { Rest.setUrl(url) Rest.put(data) .success( function(data, status, headers, config) { scope.$emit('SaveComplete', group_id, false); }) .error( function(data, status, headers, config) { scope.$emit('SaveComplete', group_id, true); ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to update group inventory source. PUT status: ' + status }); }); } } else { // No source value scope.$emit('SaveComplete', group_id, false); } }); // Cancel scope.cancelModal = function() { if (scope.searchCleanup) { scope.searchCleanup(); } } // Save scope.formModalAction = function() { Wait('start'); try { scope.formModalActionDisabled = true; // Make sure we have valid variable data if (scope.parseType == 'json') { var json_data = JSON.parse(scope.variables); //make sure JSON parses } else { var json_data = jsyaml.load(scope.variables); //parse yaml } // Make sure our JSON is actually an object if (typeof json_data !== 'object') { throw "failed to return an object!"; } var data = { name: scope['name'], description: scope['description'] }; if (inventory_id) { data['inventory'] = inventory_id; } if ($.isEmptyObject(json_data)) { data['variables'] = ""; } else { data['variables'] = JSON.stringify(json_data, undefined, '\t'); } Rest.setUrl(defaultUrl); Rest.post(data) .success( function(data, status, headers, config) { scope.$emit('formSaveSuccess', data.id, GetBasePath('inventory_sources') + data.id + '/'); }) .error( function(data, status, headers, config) { Wait('stop'); scope.formModalActionDisabled = false; ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to add new group. POST returned status: ' + status }); }); } catch(err) { Wait('stop'); scope.formModalActionDisabled = false; Alert("Error", "Error parsing group variables. Parser returned: " + err); } } scope.sourceChange = function() { SourceChange({ scope: scope, form: GroupForm }); } var choicesReady = 0; if (scope.removeChoicesReady) { scope.removeChoicesReady(); } scope.removeChoicesReady = scope.$on('choicesReadyGroup', function() { choicesReady++; if (choicesReady == 2) { $('#form-modal').modal('show'); Wait('stop'); } }); // Load options for source regions GetChoices({ scope: scope, url: GetBasePath('inventory_sources'), field: 'source_regions', variable: 'rax_regions', choice_name: 'rax_region_choices', callback: 'choicesReadyGroup' }); GetChoices({ scope: scope, url: GetBasePath('inventory_sources'), field: 'source_regions', variable: 'ec2_regions', choice_name: 'ec2_region_choices', callback: 'choicesReadyGroup' }); GetSourceTypeOptions({ scope: scope, variable: 'source_type_options' }); Wait('start'); } }]) .factory('GroupsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', 'Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName', 'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate', 'GetUpdateIntervalOptions', 'LookUpInit', 'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, GetBasePath, SetNodeName, ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, GetUpdateIntervalOptions, LookUpInit, Empty, Wait, GetChoices, UpdateGroup, SourceChange) { return function(params) { var parent_scope = params.scope; var group_id = params.group_id; var tree_id = params.tree_id; var inventory_id = params.inventory_id; var generator = GenerateForm; var form = GroupForm; var defaultUrl = GetBasePath('groups') + group_id + '/'; var master = {}; // Load the modal form var scope = generator.inject(form, { mode: 'edit', modal: true, related: false, show_modal: false }); generator.reset(); // Change the selected group if (parent_scope.selected_tree_id !== tree_id) parent_scope.showHosts(tree_id, group_id, false); GetSourceTypeOptions({ scope: scope, variable: 'source_type_options' }); scope.formModalActionLabel = 'Save'; scope.formModalHeader = 'Group'; scope.formModalCancelShow = true; scope.source = form.fields.source['default']; scope.parseType = 'yaml'; scope[form.fields['source_vars'].parseTypeName] = 'yaml'; scope.sourcePathRequired = false; ParseTypeChange(scope); ParseTypeChange(scope, 'source_vars', form.fields['source_vars'].parseTypeName); // After the group record is loaded, retrieve related data if (scope.groupLoadedRemove) { scope.groupLoadedRemove(); } scope.groupLoadedRemove = scope.$on('groupLoaded', function() { if (scope.variable_url) { // get group variables Rest.setUrl(scope.variable_url); Rest.get() .success( function(data, status, headers, config) { if ($.isEmptyObject(data)) { scope.variables = "---"; } else { scope.variables = jsyaml.safeDump(data); } Wait('stop'); }) .error( function(data, status, headers, config) { scope.variables = null; Wait('stop'); ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to retrieve group variables. GET returned status: ' + status }); }); } else { scope.variables = "---"; } master.variables = scope.variables; if (scope.source_url) { // get source data Rest.setUrl(scope.source_url); Rest.get() .success( function(data, status, headers, config) { for (var fld in form.fields) { if (fld == 'checkbox_group') { for (var i = 0; i < form.fields[fld].fields.length; i++) { var flag = form.fields[fld].fields[i]; if (data[flag.name] !== undefined) { scope[flag.name] = data[flag.name]; master[flag.name] = scope[flag.name]; } } } if (fld == 'source') { var found = false; for (var i=0; i < scope.source_type_options.length; i++) { if (scope.source_type_options[i].value == data['source']) { scope['source'] = scope.source_type_options[i]; found = true; } } if (!found || scope['source'].value == "") { scope['groupUpdateHide'] = true; } else { scope['groupUpdateHide'] = false; } master['source'] = scope['source']; } else if (fld == 'update_interval') { if (data[fld] == '' || data[fld] == null || data[fld] == undefined) { data[fld] = 0; } for (var i=0; i < scope.update_interval_options.length; i++) { if (scope.update_interval_options[i].value == data[fld]) { scope[fld] = scope.update_interval_options[i]; } } } else if (fld == 'source_vars') { // Parse source_vars, converting to YAML. if ($.isEmptyObject(data.source_vars) || data.source_vars == "\{\}" || data.source_vars == "null" || data.source_vars == "") { scope.source_vars = "---"; } else { var json_obj = JSON.parse(data.extra_vars); scope.source_vars = jsyaml.safeDump(json_obj); } master.source_vars = scope.variables; } else if (data[fld]) { scope[fld] = data[fld]; master[fld] = scope[fld]; } if (form.fields[fld].sourceModel && data.summary_fields && data.summary_fields[form.fields[fld].sourceModel]) { scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; } } scope.sourceChange(); //set defaults that rely on source value if (data['source_regions']) { if (data['source'] == 'ec2' || data['source'] == 'rax') { var set = (data['source'] == 'ec2') ? scope['ec2_regions'] : scope['rax_regions']; var opts = []; var list = data['source_regions'].split(','); for (var i=0; i < list.length; i++) { for (var j=0; j < set.length; j++) { if (list[i] == set[j].value) { opts.push({ id: set[j].value, text: set[j].label }); } } } master['source_regions'] = opts; $('#s2id_group_source_regions').select2('data', opts); } } else { // If empty, default to all master['source_regions'] = [{ id: 'all', text: 'All' }]; } scope['group_update_url'] = data.related['update']; Wait('stop'); }) .error( function(data, status, headers, config) { scope.source = ""; Wait('stop'); ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to retrieve inventory source. GET status: ' + status }); }); } }); if (scope.removeChoicesComplete) { scope.removeChoicesComplete(); } scope.removeChoicesComplete = scope.$on('choicesCompleteGroup', function() { // Retrieve detail record and prepopulate the form Rest.setUrl(defaultUrl); Rest.get() .success( function(data, status, headers, config) { for (var fld in form.fields) { if (data[fld]) { scope[fld] = data[fld]; master[fld] = scope[fld]; } } scope.variable_url = data.related.variable_data; scope.source_url = data.related.inventory_source; $('#form-modal').modal('show'); scope.$emit('groupLoaded'); }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to retrieve group: ' + defaultUrl + '. GET status: ' + status }); }); }); var choicesReady = 0; if (scope.removeChoicesReady) { scope.removeChoicesReady(); } scope.removeChoicesReady = scope.$on('choicesReadyGroup', function() { choicesReady++; if (choicesReady == 2) { scope.$emit('choicesCompleteGroup'); } }); // Load options for source regions GetChoices({ scope: scope, url: GetBasePath('inventory_sources'), field: 'source_regions', variable: 'rax_regions', choice_name: 'rax_region_choices', callback: 'choicesReadyGroup' }); GetChoices({ scope: scope, url: GetBasePath('inventory_sources'), field: 'source_regions', variable: 'ec2_regions', choice_name: 'ec2_region_choices', callback: 'choicesReadyGroup' }); Wait('start'); if (scope.removeSaveComplete) { scope.removeSaveComplete(); } scope.removeSaveComplete = scope.$on('SaveComplete', function(e, error) { if (!error) { // Update the view with any changes UpdateGroup({ scope: parent_scope, group_id: group_id, properties: { name: scope.name, description: scope.description, has_inventory_sources: (scope.source) ? true : false } }); //Clean up scope.searchCleanup(); scope.formModalActionDisabled = false; scope.showGroupHelp = false; //get rid of the Hint Wait('stop'); $('#form-modal').modal('hide'); } }); if (scope.removeFormSaveSuccess) { scope.removeFormSaveSuccess(); } scope.removeFormSaveSuccess = scope.$on('formSaveSuccess', function(e, group_id) { // Source data gets stored separately from the group. Validate and store Source // related fields, then call SaveComplete to wrap things up. var parseError = false; var saveError = false; // Update the selector tree with new group name, descr //SetNodeName({ scope: scope['selectedNode'], group_id: group_id, // name: scope.name, description: scope.description }); if (scope.source.value !== null && scope.source.value !== '') { var data = { group: group_id, source: scope['source'].value, source_path: scope['source_path'], credential: scope['credential'], overwrite: scope['overwrite'], overwrite_vars: scope['overwrite_vars'], update_on_launch: scope['update_on_launch'] //update_interval: scope['update_interval'].value }; // Create a string out of selected list of regions var regions = $('#s2id_group_source_regions').select2("data"); var r = []; for (var i=0; i < regions.length; i++) { r.push(regions[i].id); } data['source_regions'] = r.join(); if (scope['source'].value == 'ec2') { // for ec2, validate variable data try { if (scope.envParseType == 'json') { var json_data = JSON.parse(scope.source_vars); //make sure JSON parses } else { var json_data = jsyaml.load(scope.source_vars); //parse yaml } // Make sure our JSON is actually an object if (typeof json_data !== 'object') { throw "failed to return an object!"; } // Send JSON as a string if ($.isEmptyObject(json_data)) { data.source_vars = ""; } else { data.source_vars = JSON.stringify(json_data, undefined, '\t'); } } catch(err) { parseError = true; scope.$emit('SaveComplete', true); Alert("Error", "Error parsing extra variables. Parser returned: " + err); } } if (!parseError) { Rest.setUrl(scope.source_url) Rest.put(data) .success( function(data, status, headers, config) { scope.$emit('SaveComplete', false); }) .error( function(data, status, headers, config) { scope.$emit('SaveComplete', true); ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to update group inventory source. PUT status: ' + status }); }); } } else { // No source value scope.$emit('SaveComplete', false); } }); // Cancel scope.cancelModal = function() { if (scope.searchCleanup) { scope.searchCleanup(); } } // Save scope.formModalAction = function() { Wait('start'); try { var refreshHosts = false; // Make sure we have valid variable data if (scope.parseType == 'json') { var json_data = JSON.parse(scope.variables); //make sure JSON parses } else { var json_data = jsyaml.load(scope.variables); //parse yaml } // Make sure our JSON is actually an object if (typeof json_data !== 'object') { throw "failed to return an object!"; } var data = {} for (var fld in form.fields) { data[fld] = scope[fld]; } data['inventory'] = inventory_id; Rest.setUrl(defaultUrl); Rest.put(data) .success( function(data, status, headers, config) { if (scope.variables) { //update group variables Rest.setUrl(scope.variable_url); Rest.put(json_data) .success( function(data, status, headers, config) { scope.$emit('formSaveSuccess', data.id); }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to update group variables. PUT status: ' + status }); }); } else { scope.$emit('formSaveSuccess', data.id); } }) .error( function(data, status, headers, config) { Wait('stop'); ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to update group: ' + group_id + '. PUT status: ' + status }); }); } catch(err) { Wait('stop'); Alert("Error", "Error parsing group variables. Parser returned: " + err); } }; // Start the update process scope.updateGroup = function() { if (scope.source == "" || scope.source == null) { Alert('Missing Configuration', 'The selected group is not configured for updates. You must first edit the group, provide Source settings, ' + 'and then run an update.', 'alert-info'); } else if (scope.status == 'updating') { Alert('Update in Progress', 'The inventory update process is currently running for group ' + scope.summary_fields.group.name + '. Use the Refresh button to monitor the status.', 'alert-info'); } else { /*if (scope.source == 'Amazon EC2') { scope.sourceUsernameLabel = 'Access Key ID'; scope.sourcePasswordLabel = 'Secret Access Key'; scope.sourcePasswordConfirmLabel = 'Confirm Secret Access Key'; } else { scope.sourceUsernameLabel = 'Username'; scope.sourcePasswordLabel = 'Password'; scope.sourcePasswordConfirmLabel = 'Confirm Password'; }*/ InventoryUpdate({ scope: scope, group_id: group_id, url: scope.group_update_url, group_name: scope.name, group_source: scope.source.value }); } } // Change the lookup and regions when the source changes scope.sourceChange = function() { SourceChange({ scope: scope, form: GroupForm }); } } }]) .factory('GroupsDelete', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', 'Prompt', 'ProcessErrors', 'GetBasePath', 'Wait', 'BuildTree', 'Find', function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors, GetBasePath, Wait, BuildTree, Find) { return function(params) { // Delete the selected group node. Disassociates it from its parent. var scope = params.scope; var tree_id = params.tree_id; var group_id = params.group_id; var inventory_id = params.inventory_id; var node = Find({ list: scope.groups, key: 'id', val: tree_id }); var url = GetBasePath('inventory') + inventory_id + '/groups/'; if (scope.selected_tree_id != tree_id) scope.showHosts(tree_id, group_id, false); var action_to_take = function() { $('#prompt-modal').on('hidden.bs.modal', function(){ Wait('start'); }); $('#prompt-modal').modal('hide'); Rest.setUrl(url); Rest.post({ id: node.group_id, disassociate: 1 }) .success( function(data, status, headers, config) { $('#prompt-modal').off(); scope.$emit('GroupDeleteCompleted'); // Signal a group refresh to start }) .error( function(data, status, headers, config) { Wait('stop'); ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST returned status: ' + status }); }); }; Prompt({ hdr: 'Delete Group', body: '

Are you sure you want to delete group ' + node.name + '?

', action: action_to_take, 'class': 'btn-danger' }); } }]) .factory('ShowUpdateStatus', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GenerateForm', 'Prompt', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'InventoryStatusForm', 'Wait', 'Empty', function($rootScope, $location, $log, $routeParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath, FormatDate, InventoryStatusForm, Wait, Empty) { return function(params) { var group_name = params.group_name; var last_update = params.last_update; var generator = GenerateForm; var form = InventoryStatusForm; var license_error = params.license_error; var tree_id = params.tree_id; var group_id = params.group_id; var parent_scope = params.parent_scope; if (last_update == undefined || last_update == null || last_update == ''){ Wait('stop'); Alert('Missing Configuration', 'The selected group is not configured for inventory sync. ' + 'Edit the group and provide Source information.', 'alert-info'); } else { var scope = generator.inject(form, { mode: 'edit', modal: true, related: false, showModal: false }); generator.reset(); scope.formModalAction = function() { console.log('tree_id: ' + tree_id); console.log('selected_tree_id: ' + parent_scope.selected_tree_id); $('#form-modal').modal("hide"); if (parent_scope && parent_scope.showHosts && !Empty(tree_id)) { if (parent_scope.selected_tree_id !== tree_id) parent_scope.showHosts(tree_id, group_id, false); } } if (scope.removeUpdateStatusReady) { scope.removeUpdateStatusReady(); } scope.removeUpdateStatusReady = scope.$on('UpdateStatusReady', function(e) { scope.formModalActionLabel = 'OK'; scope.formModalCancelShow = false; scope.formModalInfo = false; scope.formModalHeader = group_name + ' - Inventory Sync'; $('#form-modal').modal('show'); Wait('stop'); }); // Retrieve detail record and prepopulate the form Rest.setUrl(last_update); Rest.get() .success( function(data, status, headers, config) { for (var fld in form.fields) { if (data[fld]) { if (fld == 'created') { scope[fld] = FormatDate(new Date(data[fld])); } else { scope[fld] = data[fld]; } } } scope.license_error = license_error; scope.$emit('UpdateStatusReady'); }) .error( function(data, status, headers, config) { $('#form-modal').modal("hide"); ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve last update: ' + last_update + '. GET status: ' + status }); }); } } }]);