Name

global.CloudServiceAccountPatterns

Description

Trigger execution of patterns associated with cmdb_ci_cloud_service_account as part of execution of discovery schedule of type Cloud Resources

Script

var CloudServiceAccountPatterns = Class.create();
CloudServiceAccountPatterns.prototype = {
  initialize: function() {
  	var logger;
  },

  /*
  * find the MID server that matches the cloud type based on the service account
  */
  findMidByServiceAccount: function (service_account) {
  	// Get glide record of the service account
  	var serviceAccountGr = new GlideRecord('cmdb_ci_cloud_service_account');
  	if (!serviceAccountGr.get(service_account)) {
  		logger.warn('Service account not found: ' + service_account);
  		return;
  	}
  	// find provider type by the service account datacenter type
  	var capiProviderGr = new GlideRecord('sn_capi_provider');
  	capiProviderGr.addQuery('datacenter_class', serviceAccountGr.datacenter_type);
  	capiProviderGr.query();
  	if (!capiProviderGr.next()) {
  		logger.warn("Provider type not found. datacenter_type: " + serviceAccountGr.datacenter_type);
  		return;
  	}
  	var service_account_id = serviceAccountGr.getValue("account_id");
  	var invocationContext = {};
  	invocationContext['service_account_id'] = service_account_id;
  	var cmdMidSelector = new global.CloudMidSelectionApi();
  	var mid_id = cmdMidSelector.selectMid(null, capiProviderGr.name, null, JSON.stringify(invocationContext));

  	if (!mid_id) {
  		logger.warn("MID not found for schedule");
  		return;
  	}
  	
  	// Find the mid name
  	var midGr = new GlideRecord('ecc_agent');
  	if (!midGr.get(mid_id)) {
  		logger.warn('MID ' + mid_id + ' not found in table ecc_agent');
  		return null;
  	}
  	var agent = midGr.name;
  	return agent;
  
  },
  
  getCredAliasNames: function (tags) {
  	if (!tags)
  		return;
  	var names = [];
  	var gr = new GlideRecord('sys_alias');
  	gr.addQuery('sys_id', tags);
  	gr.query();
  	while (gr.next()) {
  		names.push(gr.id);
  	}
  	return names.join(",");
  },

  getDatacenterTypeBySchedule: function(discoverySchedule) {
  	var ldcConfigGr = new GlideRecord('cmp_discovery_ldc_config');
  	ldcConfigGr.addQuery('discovery_schedule', discoverySchedule);
  	ldcConfigGr.addNotNullQuery('service_account.datacenter_type');
  	ldcConfigGr.setLimit(1);
  	ldcConfigGr.query();

  	if (!ldcConfigGr.next()) {
  		logger.warn('No record found in cmp_discovery_ldc_config having a service account with configured datacenter type');
  		return null;
  	}
  	return ldcConfigGr.service_account.datacenter_type + '';
  },
  
  getDatacenterTypeByAccountList: function(serviceAccounts) {
  	var serviceAccountGr = new GlideRecord('cmdb_ci_cloud_service_account');
  	serviceAccountGr.addQuery('sys_id', 'IN', serviceAccounts);
  	serviceAccountGr.addNotNullQuery('datacenter_type');
  	serviceAccountGr.setLimit(1);
  	serviceAccountGr.query();

  	if (!serviceAccountGr.next()) {
  		logger.warn('No record found in cmdb_ci_cloud_service_account with a valid datacenter type'); 
  		return null;
  	}
  	return serviceAccountGr.datacenter_type + '';
  },
  
  // Get all Infra patterns of service account associated. Consult sa_ci_to_map in order to avoid running pattern of wrong cloud
  getPatternsToRun: function(provider, excludedPatterns) {
  	if (!provider) {
  		logger.warn('No provider was provided. Can\'t perform lookup for service account patterns to execute');
  		return [];
  	}

  	if (!excludedPatterns)
  		excludedPatterns = [];

  	var patternsToRun = [];
  	var saCiToPattern = new GlideRecord('sa_ci_to_pattern');
  	saCiToPattern.addQuery('ci_type', provider);
  	saCiToPattern.addQuery('pattern.active', '1');
  	saCiToPattern.addQuery('pattern.cpattern_type','3');
  	saCiToPattern.addQuery('pattern.ci_type','cmdb_ci_cloud_service_account');
  	saCiToPattern.addQuery('pattern', 'NOT IN', excludedPatterns);
  	saCiToPattern.query();
  	
  	while (saCiToPattern.next())
  		patternsToRun.push(saCiToPattern.pattern + '');
  	
  	return patternsToRun;
  },

  getAccountsToLDCsMap: function (ldcList) {
  	if (!ldcList)
  		return {};
  	var ldcArr = ldcList.split(',');
  	
  	if (ldcArr.length == 0)
  		return {};

  	var ldcsChunkSize = 100,
  		ldcsChunk,
  		accountToLDCs = {};

  	while (ldcArr.length) {
  		ldcsChunk = ldcArr.splice(0, ldcsChunkSize);

  		var relGr = new GlideRecord('cmdb_rel_ci');
  		relGr.addQuery('parent', 'IN', ldcsChunk);
  		relGr.addQuery('type', '5f985e0ec0a8010e00a9714f2a172815'); // Hosted-on
  		relGr.query();

  		while (relGr.next()) {
  			var serviceAccount = relGr.child + '';

  			/* 
  			 * We check if the account already exists because the same account
  			 * can show up multiple times in cmp_discovery_ldc_config for different LDCs
  			 * See attached pic at DEF0092784 [DEF0092784_3SubAccounts_3LDCs.png]
  			 */
  			if (!accountToLDCs.hasOwnProperty(serviceAccount))
  				accountToLDCs[serviceAccount] = [];

  			accountToLDCs[serviceAccount].push(relGr.parent + '');
  		}
  	}
  	
  	// Remove accounts that are not valid anymore [in case database has bad data for some reason]
  	var serviceAccounts = Object.keys(accountToLDCs);
  	
  	for (var idx in serviceAccounts) {
  		var serviceAccountGlideRecord = new GlideRecord('cmdb_ci_cloud_service_account');
  		
  		if (!serviceAccountGlideRecord.get('sys_id', serviceAccounts[idx]))
  			delete accountToLDCs[serviceAccounts[idx]];
  	}
  	
  	return accountToLDCs;
  },

  /*
   * Find all the infrastructure patterns associated with CI type cmdb_ci_cloud_service_account and trigger them
   */
  runServiceAccountPatterns: function (discoveryStatus, ldcList) {
  	logger = new DiscoveryLogger(discoveryStatus);
  	
  	// Load the discovery status record
      var statusGr = new GlideRecord('discovery_status');
      if (!statusGr.get(discoveryStatus)) {
  		logger.warn('Discovery status ' + discoveryStatus + ' not found');
          return;
  	}

  	/*
  	 * Find the discovery schedule from the discovery status
  	 * In case of an invalid schedule we still want to try and get accounts.
  	 * because it may indicate we are on 'Discover Now' type of discovery
  	 * triggered by CMP's admin screen
  	 */
  	var discoveryScheduleGlideRecord = new GlideRecord('discovery_schedule');
      var discoverySchedule = statusGr.dscheduler + '';

      if (!discoveryScheduleGlideRecord.get('sys_id', discoverySchedule))
  		discoverySchedule = null;

  	/*
  	 * DEF0107342
  	 * Had to comment the below section to provide a workaround for the defect
  	 * This code should stay here to be uncommented\modified for future fix
  	 *
  	 * var serviceAccounts = [];
  	 * // Find service accounts
  	 * if (discoverySchedule != null)
  	 *  serviceAccounts = this.findServiceAccountsBySchedule(discoverySchedule);
  	 * else
  	 *	serviceAccounts = this.findServiceAccountsByLDCs(ldcList);
  	 */

  	var accountToLDCs = this.getAccountsToLDCsMap(ldcList);
  	var serviceAccounts = Object.keys(accountToLDCs);
  	
  	// If for some reason [shouldn't happen] we have no accounts back, use the schedule
  	if (serviceAccounts.length == 0)
  		serviceAccounts = this.findServiceAccountsBySchedule(discoverySchedule);

      if (serviceAccounts.length == 0) {
  		logger.warn('No service acocunt defined on cmp_discovery_ldc_config for schedule "' + statusGr.dscheduler.name + '"');
  		return;
  	}

  	var datacenterType;

  	/*
  	 * If we have a schedule, find datacenter type using cmp_discovery_ldc_config
  	 * Datacenter type should be the same for a single schedule
  	 * Otherwise, grab it from the accounts found
  	 * This helps take out some queries of the loop: Exclude list & CIs to pattern
  	 */
  	if (discoverySchedule != null)
  		datacenterType = this.getDatacenterTypeBySchedule(discoverySchedule);
  	else
  		datacenterType = this.getDatacenterTypeByAccountList(serviceAccounts);

  	if (datacenterType == null)
  		return;

  	// Build exclude list
  	var grExclude = new GlideRecord('sa_cloud_topology_discovery_pattern');
  	grExclude.addQuery('datacenter_type', datacenterType);
  	grExclude.query();

  	var excludedPatterns = [];
  	while (grExclude.next())
  		excludedPatterns.push(grExclude.pattern + '');

  	var patternsToRun = this.getPatternsToRun(datacenterType, excludedPatterns);
  	var cad = new CloudApplicationDiscovery();
  	// Prepare and trigger all infrastructure patterns for all service accounts found
  	for (var idx in serviceAccounts) {
  		var serviceAccountId = serviceAccounts[idx];

  		serviceAccountGr = new GlideRecord('cmdb_ci_cloud_service_account');
  		if (!serviceAccountGr.get(serviceAccountId)) {
  			logger.warn('Service account ' + serviceAccountId + ' not found'); 
  			continue;
  		}
  		
  		var ppe = cad.createPreExecutionData(serviceAccountId, null);
  		ppe.addString('cloud_account_id', serviceAccountGr.account_id);
  		ppe.addString('cloud_datacenter_type', serviceAccountGr.datacenter_type);
  		ppe.addString('cloud_datacenter_url', serviceAccountGr.datacenter_url);

  		if (serviceAccountGr.discovery_credentials) {
  			var credGr = new GlideRecord('discovery_credentials');
  			if (credGr.get(serviceAccountGr.discovery_credentials)) {
  				ppe.addString('cloud_cred_name', credGr.name);
  				ppe.addString('cloud_cred_id', serviceAccountGr.discovery_credentials);
  				ppe.addString('cloud_cred_alias', this.getCredAliasNames(credGr.tag));
  			}
  		}

  		// Fill cmp_discovery_ldc_config on the pattern context 
  		ldcConfigGr = new GlideRecord('cmp_discovery_ldc_config');
  		ldcConfigGr.addQuery('service_account', serviceAccountId);
  		// Same account can be on different schedules. We want to get info only for current schedule
  		ldcConfigGr.addQuery('discovery_schedule', discoverySchedule);
  		ldcConfigGr.query();

  		while (ldcConfigGr.next())
  			ppe.addTableEntry('cmp_discovery_ldc_config',this.glideRecordToMap(ldcConfigGr));

  		// If we were given list of LDCs, place them on the pattern context
  		if (accountToLDCs[serviceAccountId]) {
  			var ldcGr = new GlideRecord(serviceAccountGr.datacenter_type);
  			ldcGr.addQuery('sys_id', 'IN', accountToLDCs[serviceAccountId]);
  			ldcGr.query();

  			while (ldcGr.next())
  				ppe.addTableEntry(serviceAccountGr.datacenter_type, this.glideRecordToMap(ldcGr));
  		}				

  		// Now trigger each of the patterns
  		for (var patternIdx in patternsToRun) {
  			var patternSysId = patternsToRun[patternIdx];
  			this.runServiceAccountPattern(serviceAccountId, serviceAccountGr.name + '', discoveryStatus, statusGr.dscheduler + '', patternSysId, ppe);
  		}
  	}
  },

 /*
  * run single pattern associated with cmdb_ci_cloud_service_account
  */
  runServiceAccountPattern: function (service_account, service_account_name, status, discovery_schedule, pattern_id, ppe) {
  	var agent;
  		
  	try {
  		agent = new CloudResourceDiscoveryUtil().getSelectedMidFromSchedule(discovery_schedule);
  		if(!agent) 
  			agent = this.findMidByServiceAccount(service_account);
  	} catch (e) {
  		logger.warn('No MID found for service account ' + service_account + '. ' + e.getMessage());
  	}
  	if (!agent) {
  		logger.warn('No MID found for service account ' + service_account);
  		return;
  	}
  	var probeParams = {};
  	var cad = new CloudApplicationDiscovery();
  	cad.launchPatternCore(service_account, service_account_name, status, discovery_schedule, pattern_id, agent, probeParams, ppe);	
  },

 /*
  * convert glide record to javascript map
  */
  glideRecordToMap: function (gr) {
  	var map = {};
  	var iterator = gr.getFields().iterator();
  	while (iterator.hasNext()) {
  		var field = iterator.next();
  		if (JSUtil.notNil(gr.getValue(field.getName()))) {
  			var fieldName = field.getName() + '';
  			if (fieldName.indexOf('sys_') != 0 || fieldName == 'sys_class_name' || fieldName == 'sys_id')
  			    map[field.getName()] = gr.getValue(field.getName());
  		}
  	}
  	map['sys_id'] = gr.getUniqueValue();
  	return map;
  },

  /*
   * DEF0107342
   * Not being called
   * This code should stay here for future fix
   */
  findServiceAccountsByLDCs: function (ldcList) {
  	if (!ldcList)
  		return [];
  	var ldcArr = ldcList.split(',');
  	
  	if (ldcArr.length == 0)
  		return [];

  	var serviceAccounts = [];
  	var relGr = new GlideRecord('cmdb_rel_ci');
  	relGr.addQuery('parent', 'IN', ldcArr);
  	relGr.addQuery('type', '5f985e0ec0a8010e00a9714f2a172815'); // Hosted-on
  	relGr.query();

  	while (relGr.next()) {
  		var serviceAccount = relGr.child + '';
  		
  		/* 
  		 * We check if the account already exists because the same account
  		 * can show up multiple times in cmp_discovery_ldc_config for different LDCs
  		 * See attached pic at DEF0092784 [DEF0092784_3SubAccounts_3LDCs.png]
  		 */
  		if (serviceAccounts.indexOf(serviceAccount) == -1)
  			serviceAccounts.push(serviceAccount);
  	}
  	
  	return serviceAccounts;
  },

  findServiceAccountsBySchedule: function (discoverySchedule) {
  	/*
  	 * Checking only "Cloud Wizard" table to fetch all accounts
  	 * PRB1310674, which was fixed in NY, is redirecting schedule creation from the service account's
  	 * form to "Cloud Wizard", hence even old way of creating schedules will create entries
  	 * in cmp_discovery_ldc_config from NY and above
  	 */
      var ldcConfigGr = new GlideRecord('cmp_discovery_ldc_config');
      ldcConfigGr.addQuery('discovery_schedule', discoverySchedule);
      ldcConfigGr.query();
      
      var serviceAccounts = [];
  	
  	while (ldcConfigGr.next()) {
  		var serviceAccount = ldcConfigGr.service_account + '';
  		
  		/* 
  		 * We check if the account already exists because the same account
  		 * can show up multiple times in cmp_discovery_ldc_config for different LDCs
  		 * See attached pic at DEF0092784 [DEF0092784_3SubAccounts_3LDCs.png]
  		 */
  		if (serviceAccounts.indexOf(serviceAccount) == -1)
  			serviceAccounts.push(serviceAccount);
  	}

  	return serviceAccounts;
  },
  
  type: 'CloudServiceAccountPatterns'
};

Sys ID

ba115ad9c3a123003e76741e81d3ae9d

Offical Documentation

Official Docs: