一、申请客户端ID和客户端密钥信息
在 All 中搜索 System Oauth ,选择 Application Registry 进入应用页面。
进入页面后,点击 New 创建用于与YMCS平台同步数据的Client ID和Client Secret信息。
选择Create an OAuth API endpoint for external clients。
创建Client ID和Client Secret。填写Name、Client Secret、Refresh Token Lifespan、Access Token Lifespan、Token Format、Scope Restriction,随后点击 Submit(提交)。
各字段说明:
名称(Name):OAuth 应用名称,用于标识当前 OAuth 客户端应用的自定义唯一名称,区分不同第三方集成客户端。
客户端密钥(Client Secret):OAuth 应用的客户端密钥,与客户端 ID 配对用于 OAuth 鉴权,系统推荐密钥最少 32 位字符,提升安全性。
可访问应用范围(Accessible from):限定可使用该 OAuth 凭证的应用域,All application scopes 代表全应用域可调用,This application scope only 代表仅当前应用域可用。
启用状态(Active):控制 OAuth 客户端是否生效,勾选启用可正常执行令牌鉴权,取消勾选则停用该客户端全部 OAuth 访问能力。
刷新令牌有效期(Refresh Token Lifespan):签发的刷新令牌有效时长(单位:秒),过期后无法换取新访问令牌,需重新发起授权流程。
访问令牌有效期(Access Token Lifespan):签发的访问令牌有效时长(单位:秒),承载平台接口调用权限,超时后自动失效。
令牌格式(Token Format):生成访问令牌的格式,可选 None(无格式)、Opaque(不透明随机字符串令牌,第三方对接通用标准)。
作用域权限限制(Scope Restriction):OAuth 客户端权限边界管控,Securely scoped 为严格最小权限模式(推荐),Broadly scoped 开放宽泛系统资源访问权限。
说明
记住这里的 Client ID 和 Client Secret 信息。
提示
Client Secret的长度应为32到128个字符。
二、创建报警同步用户
在 All 中搜索 User Administration ,选择 Users 进入用户管理页面。
点击 New ,创建用于YMCS平台同步数据的用户信息。
填写 User ID , First name , Last name ,然后点击 Save 创建用户。
说明
此处创建的用户将成为事件工单记录的呼叫者。
点击 Set Password > Generate > Save Password 以设置密码。
说明
生成后,您可以点击复制当前生成的密码。
保存后,默认需要重置密码。您可以选择使用账户登录来更改账户密码,也可以选择取消选中Password need reset以更新密码。
添加角色。在 Roles 选项卡上点击 Edit ,搜索incident_manager,点击 > Save。
点击Save。
三、在YMCS上进行配置
登录YMCS。
点击 系统管理 > 集成管理 > 第三方服务。
在Servicenow模块中,点击启用。
选择对接的平台并输入相关信息,点击保存。
说明
仅被选择的平台的告警会同步至Servicenow平台。
四、配置Servicenow回调
功能简介
在配置回调之后,Servicenow的故障关闭/取消操作将通过YMCS的OpenAPI与告警状态进行同步。
获取OpenAPI访问密钥
登录YMCS。
点击 系统设置 > 集成管理 > API。
配置回调脚本
在 All 中搜索 Business Rules并进入。
点击 New。
设置信息。
名称:输入名称。
表格:选择Incident。
选择 Advanced。
运行时机:
When:选择 After。
过滤条件:将 State 字段设置为 changes。
配置脚本。在 Advanced 选项卡中配置回调脚本。
点击 Submit。
说明
此处API地址、accessKeyId和accessKeySecret需要根据你的YMCS企业账户配置。
(function executeRule(current, previous /*null when async*/ ) {
// openapi address
var endpoint = 'your openapi address';
// Replace with your access key information
accessKeyId='your access key id';
accessKeySecret='your access key secret';
changeYMCSAlarmStatus(endpoint, accessKeyId, accessKeySecret, current, previous);
})(current, previous);
function changeYMCSAlarmStatus(endpoint, accessKeyId, accessKeySecre, current, previous){
// Only handles update status and the status is closed or cancelled
if (!(current && previous) || !(current.state == 7 || current.state == 8)) {
return;
}
// Check whether the configuration is configured
if(!isVaildConfiguration(endpoint, accessKeyId, accessKeySecret)){
return;
}
// No processing will be performed if the status has not changed.
if (current.state == previous.state) {
return;
}
var token = getTokenCache(endpoint, accessKeyId, accessKeySecret);
if(!token) {
return;
}
// Only handles closing and cancelling state sync
// Closed ==> Resolved
// Canceled ==> Ignored
if (current.state == 7) {
synchronousIncidentStatus(endpoint, token, current.correlation_id, 2);
} else if (current.state == 8) {
synchronousIncidentStatus(endpoint, token, current.correlation_id, 3);
}
}
// Check whether the configuration is configured
function isVaildConfiguration(endpoint, accessKeyId, accessKeySecret){
if(!endpoint ||!accessKeyId || !accessKeySecret
|| endpoint == '' ||accessKeyId == '' || accessKeySecret == ''){
return false;
}
return true;
}
// Synchronize the alarm status of YMCS platform
function synchronousIncidentStatus(endpoint, token, alarmId, status){
gs.info('[YMCS] synchronous incident status start');
if(!token || !alarmId || alarmId == '') {
return;
}
var alarmOpenApi= endpoint + '/v2/dm/alarm/changeStatus';
var authorization = token['token_type'] + ' ' + token['access_token'];
var headers = {
Authorization: authorization,
timestamp: new Date().getTime(),
nonce: getNonce()
};
var requestBody = {
ids: [alarmId + ''],
status: status + ''
};
var response = request('PATCH', alarmOpenApi, requestBody, headers);
if(!response) {
gs.warn('[YMCS] Synchronous incident status response is empty');
return;
}
var responseObject = JSON.parse(response);
if(isError(responseObject)) {
gs.warn('[YMCS] Synchronous incident failure, response: ' + JSON.stringify(response));
} else {
gs.info('[YMCS] synchronous incident status end, responseObject: '+ JSON.stringify(response));
}
}
// Get authorization token
function getTokenCache(endpoint, accessKeyId, accessKeySecret){
var TOKEN_TYPE_KEY = 'YMCS_CACHE_TOKEN';
var ACCESS_TOKEN_KEY = 'YMCS_CACHE_ACCESS_TOKEN';
var EXPIRY_KEY = 'YMCS_CACHE_EXPIRY';
var tokenType = gs.getProperty(TOKEN_TYPE_KEY);
var accessToken = gs.getProperty(ACCESS_TOKEN_KEY);
var expiry = gs.getProperty(EXPIRY_KEY);
if (accessToken && expiry) {
var now = new Date().getTime();
if (expiry > now) {
return {
'token_type': tokenType,
'access_token': accessToken
};
}
}
// If there is no valid token, get a new one
var newToken = getToken(endpoint, accessKeyId, accessKeySecret);
if(isError(newToken)) {
gs.warn('[YMCS] Get token error:'+ JSON.stringify(newToken));
return undefined;
}
// Cache the new token to system properties as cache
gs.setProperty(TOKEN_TYPE_KEY, newToken['token_type']);
gs.setProperty(ACCESS_TOKEN_KEY, newToken['access_token']);
var expiryTimestamp = new Date().getTime() + (newToken['expires_in'] * 1000) - 10000;
gs.setProperty(EXPIRY_KEY, expiryTimestamp);
return newToken;
}
// Get authorization token
function getToken(endpoint, accessKeyId, accessKeySecret){
var tokenApi= endpoint + '/v2/token';
var requestBody = {
grant_type: 'client_credentials'
};
var headers = {
Authorization: getBasicAuth(accessKeyId, accessKeySecret),
timestamp: new Date().getTime(),
nonce: getNonce()
};
var response = request('POST', tokenApi, requestBody, headers);
if(!response) {
gs.warn('[YMCS] Get token response is empty');
return undefined;
}
var tokenObject = JSON.parse(response);
if (isError(tokenObject)) {
gs.warn('[YMCS] Failed to get token, response:' + JSON.stringify(tokenObject));
return undefined;
}
return tokenObject;
}
// clear token cache
function clearTokenCache(){
gs.setProperty('YMCS_CACHE_TOKEN', '');
gs.setProperty('YMCS_CACHE_ACCESS_TOKEN', '');
gs.setProperty('YMCS_CACHE_EXPIRY', '');
}
// Request Data
function request(method, endpoint, requestBody, headers){
// Create a GlideHTTPRequest object
var httpRequest = new sn_ws.RESTMessageV2();
// Set the endpoint for REST messages
httpRequest.setEndpoint(endpoint);
// Set the HTTP method to POST
httpRequest.setHttpMethod(method);
// Set the HTTP headers
if (headers) {
for (var key in headers) {
if (headers.hasOwnProperty(key)) {
httpRequest.setRequestHeader(key, headers[key]);
}
}
}
// Send a request and get a response
httpRequest.setRequestHeader("Content-Type", "application/json");
httpRequest.setRequestBody(JSON.stringify(requestBody));
var httpResponse = httpRequest.execute();
// Get the response body (body)
return httpResponse.getBody();
}
// Build authentication information
function getBasicAuth(accessKeyId, accessKeySecret) {
var credentials = accessKeyId + ':' + accessKeySecret;
var base64Credentials = GlideStringUtil.base64Encode(credentials);
return 'Basic ' + base64Credentials;
}
// generateUUID
function getNonce() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// is error
function isError(responseObject){
if (responseObject && !responseObject.hasOwnProperty('error')) {
return false;
}
if (responseObject && (!responseObject['error'] || responseObject['error'] == '')) {
return false;
}
clearTokenCache();
return true;
}