유량제어 현황 모니터링 기능 추가
This commit is contained in:
@@ -16,173 +16,486 @@
|
|||||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||||
|
|
||||||
<script language="javascript" >
|
<!-- 페이지 커스텀 스타일 (fragment) -->
|
||||||
|
<jsp:include page="fragments/inflowGroupDetailStyle.jsp"/>
|
||||||
|
|
||||||
|
<script language="javascript">
|
||||||
var url ='<c:url value="/onl/admin/inflow/inflowAdapterControlMan.json" />';
|
var url ='<c:url value="/onl/admin/inflow/inflowAdapterControlMan.json" />';
|
||||||
var url_view ='<c:url value="/onl/admin/inflow/inflowAdapterControlMan.view" />';
|
var url_view ='<c:url value="/onl/admin/inflow/inflowAdapterControlMan.view" />';
|
||||||
var isDetail = false;
|
var isDetail = false;
|
||||||
function init(url,key,callback){
|
|
||||||
$.ajax({
|
function updateThresholdLabel() {
|
||||||
type : "POST",
|
var timeUnit = $('select[name=thresholdTimeUnit]').val();
|
||||||
url:url,
|
var $input = $('#thresholdInput');
|
||||||
dataType:"json",
|
var $unit = $('#thresholdUnit');
|
||||||
data:{cmd: 'LIST_INIT_COMBO'},
|
|
||||||
success:function(json){
|
var unitLabels = {
|
||||||
|
'': '',
|
||||||
|
'SEC': 'req/sec',
|
||||||
|
'MIN': 'req/min',
|
||||||
|
'HOU': 'req/hour',
|
||||||
|
'DAY': 'req/day',
|
||||||
|
'MON': 'req/month'
|
||||||
|
};
|
||||||
|
|
||||||
|
$unit.text(unitLabels[timeUnit] || 'requests');
|
||||||
|
|
||||||
|
if (timeUnit === '') {
|
||||||
|
$input.prop('disabled', true).val('');
|
||||||
|
$unit.css('visibility', 'hidden');
|
||||||
|
} else {
|
||||||
|
$input.prop('disabled', false);
|
||||||
|
$unit.css('visibility', 'visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(url, key, callback) {
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
dataType: "json",
|
||||||
|
data: {cmd: 'LIST_INIT_COMBO'},
|
||||||
|
success: function(json) {
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
|
updateThresholdLabel();
|
||||||
if(typeof callback === 'function') {
|
|
||||||
callback(url,key);
|
if (typeof callback === 'function') {
|
||||||
|
callback(url, key);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function detail(url,key){
|
|
||||||
if (!isDetail)return;
|
function detail(url, key) {
|
||||||
$.ajax({
|
if (!isDetail) return;
|
||||||
type : "POST",
|
$.ajax({
|
||||||
url:url,
|
type: "POST",
|
||||||
dataType:"json",
|
url: url,
|
||||||
data:{cmd: 'DETAIL', name : key},
|
dataType: "json",
|
||||||
success:function(json){
|
data: {cmd: 'DETAIL', name: key},
|
||||||
|
success: function(json) {
|
||||||
var data = json;
|
var data = json;
|
||||||
$("input[name=name]").attr('readonly',true);
|
$("input[name=name]").attr('readonly', true);
|
||||||
$("input[name=desc]").attr('readonly',true);
|
$("input[name=desc]").attr('readonly', true);
|
||||||
|
|
||||||
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function(){
|
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function() {
|
||||||
var name = $(this).attr("name");
|
var name = $(this).attr("name");
|
||||||
var tag = $(this).prop("tagName").toLowerCase();
|
var tag = $(this).prop("tagName").toLowerCase();
|
||||||
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateThresholdLabel();
|
||||||
|
|
||||||
|
var modifiedAt = data.MODIFIEDAT || data.modifiedAt;
|
||||||
|
if (modifiedAt) {
|
||||||
|
$('#modifiedAtText').text(modifiedAt);
|
||||||
|
$('#modifiedAtInfo').show();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$(document).ready(function() {
|
|
||||||
var returnUrl = getReturnUrlForReturn();
|
function switchTab(tabName) {
|
||||||
var key ="${param.name}";
|
$('.detail-tab').removeClass('active');
|
||||||
if (key != "" && key !="null"){
|
$('.detail-tab[data-tab="' + tabName + '"]').addClass('active');
|
||||||
isDetail = true;
|
$('.tab-content').removeClass('active');
|
||||||
}
|
$('#tab' + tabName.charAt(0).toUpperCase() + tabName.slice(1) + 'Content').addClass('active');
|
||||||
init(url,key,detail);
|
|
||||||
|
if (tabName === 'status' && isDetail) {
|
||||||
|
loadBucketStatus();
|
||||||
$("#btn_modify").click(function(){
|
}
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
}
|
||||||
if (isDetail){
|
|
||||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
function loadBucketStatus() {
|
||||||
}else{
|
$('#bucketServerList').html('<div class="bucket-empty-msg"><i class="material-icons">hourglass_empty</i>조회 중...</div>');
|
||||||
postData.push({ name: "cmd" , value:"INSERT"});
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: url,
|
||||||
|
data: { cmd: 'LIST_BUCKET_STATUS' },
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(result) {
|
||||||
|
renderBucketStatus(result.servers || []);
|
||||||
|
$('#lastRefreshTime').text(formatDateTime(new Date()));
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
$('#bucketServerList').html('<div class="bucket-empty-msg bucket-error-msg"><i class="material-icons">error_outline</i>조회 실패: ' + e.statusText + '</div>');
|
||||||
}
|
}
|
||||||
$.ajax({
|
});
|
||||||
type : "POST",
|
}
|
||||||
url:url,
|
|
||||||
data:postData,
|
function formatDateTime(date) {
|
||||||
success:function(args){
|
var h = date.getHours();
|
||||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
var m = date.getMinutes();
|
||||||
goNav(returnUrl);//LIST로 이동
|
var s = date.getSeconds();
|
||||||
},
|
return (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
|
||||||
error:function(e){
|
}
|
||||||
alert(e.responseText);
|
|
||||||
|
function renderBucketStatus(servers) {
|
||||||
|
var container = $('#bucketServerList');
|
||||||
|
container.empty();
|
||||||
|
|
||||||
|
if (servers.length === 0) {
|
||||||
|
container.html('<div class="bucket-empty-msg"><i class="material-icons">dns</i>등록된 서버가 없습니다</div>');
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var onlineCount = 0;
|
||||||
|
var perSecondCapacity = 0;
|
||||||
|
var thresholdCapacity = 0;
|
||||||
|
var thresholdTimeUnit = '';
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
if (server.status === 'online' && server.data && server.data.buckets) {
|
||||||
|
onlineCount++;
|
||||||
|
$.each(server.data.buckets, function(j, bucket) {
|
||||||
|
if (bucket.type === 'perSecond') {
|
||||||
|
perSecondCapacity += bucket.capacity || 0;
|
||||||
|
} else if (bucket.type === 'threshold') {
|
||||||
|
thresholdCapacity += bucket.capacity || 0;
|
||||||
|
if (!thresholdTimeUnit) thresholdTimeUnit = bucket.timeUnit;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (onlineCount > 0) {
|
||||||
|
$('#summaryInstanceCount').html(onlineCount + '<span class="unit">개</span>');
|
||||||
|
|
||||||
|
var configPerSecond = parseInt($('input[name=thresholdPerSecond]').val()) || 0;
|
||||||
|
var configThreshold = parseInt($('input[name=threshold]').val()) || 0;
|
||||||
|
|
||||||
|
if (configPerSecond > 0) {
|
||||||
|
$('#summaryPerSecond').html(formatNumber(perSecondCapacity) + '<span class="unit">req/sec</span>');
|
||||||
|
$('#summaryPerSecondCalc').text(formatNumber(configPerSecond) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryPerSecondWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryPerSecondWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configThreshold > 0) {
|
||||||
|
var timeUnitLabel = getTimeUnitText(thresholdTimeUnit);
|
||||||
|
$('#summaryThreshold').html(formatNumber(thresholdCapacity) + '<span class="unit">req/' + timeUnitLabel + '</span>');
|
||||||
|
$('#summaryThresholdCalc').text(formatNumber(configThreshold) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryThresholdWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryThresholdWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#bucketSummary').show();
|
||||||
|
} else {
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
var statusClass = server.status || 'offline';
|
||||||
|
var statusText = { online: '정상', offline: '오프라인', no_data: '데이터 없음' };
|
||||||
|
|
||||||
|
var card = '<div class="bucket-server-card">' +
|
||||||
|
'<div class="bucket-server-header">' +
|
||||||
|
'<div>' +
|
||||||
|
'<span class="bucket-server-name">' + server.serverName + '</span> ' +
|
||||||
|
'<span class="bucket-server-ip">' + server.serverIp + ':' + server.serverPort + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<span class="bucket-server-status ' + statusClass + '">' + (statusText[statusClass] || statusClass) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-server-body">' + renderBucketBody(server) + '</div>' +
|
||||||
|
'</div>';
|
||||||
|
container.append(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBucketBody(server) {
|
||||||
|
if (server.status === 'offline') {
|
||||||
|
return '<div class="bucket-error-msg">' + (server.message || '서버 연결 실패') + '</div>';
|
||||||
|
}
|
||||||
|
if (server.status === 'no_data') {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;"><i class="material-icons" style="font-size:18px;">info_outline</i> ' + (server.message || '어댑터가 로드되지 않음') + '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = server.data;
|
||||||
|
if (!data || !data.buckets || data.buckets.length === 0) {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;">버킷 정보 없음</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '<div class="bucket-info-grid">';
|
||||||
|
$.each(data.buckets, function(j, bucket) {
|
||||||
|
var typeLabel = bucket.type === 'perSecond' ? '초당 임계치' : '추가 임계치';
|
||||||
|
var timeUnitText = getTimeUnitText(bucket.timeUnit);
|
||||||
|
var usagePercent = bucket.usagePercent || 0;
|
||||||
|
var progressClass = usagePercent < 50 ? 'low' : (usagePercent < 80 ? 'medium' : 'high');
|
||||||
|
|
||||||
|
html += '<div class="bucket-card">' +
|
||||||
|
'<div class="bucket-card-header">' +
|
||||||
|
'<span class="bucket-type">' + typeLabel + '</span>' +
|
||||||
|
'<span class="bucket-type-badge">' + timeUnitText + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-progress-wrap">' +
|
||||||
|
'<div class="bucket-progress-bar">' +
|
||||||
|
'<div class="bucket-progress-fill ' + progressClass + '" style="width:' + usagePercent + '%"></div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-stats">' +
|
||||||
|
'<span>사용량: <span class="bucket-stats-value">' + usagePercent.toFixed(1) + '%</span></span>' +
|
||||||
|
'<span>잔여: <span class="bucket-stats-value">' + formatNumber(bucket.availableTokens) + '</span> / ' + formatNumber(bucket.capacity) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimeUnitText(timeUnit) {
|
||||||
|
var units = {SEC: '초', MIN: '분', HOU: '시간', DAY: '일', MON: '월'};
|
||||||
|
return units[timeUnit] || timeUnit || '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(num) {
|
||||||
|
if (num === undefined || num === null) return '-';
|
||||||
|
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
var returnUrl = getReturnUrlForReturn();
|
||||||
|
var key = "${param.name}";
|
||||||
|
if (key != "" && key != "null") {
|
||||||
|
isDetail = true;
|
||||||
|
}
|
||||||
|
init(url, key, detail);
|
||||||
|
|
||||||
|
$("select[name=thresholdTimeUnit]").on('change', function() {
|
||||||
|
updateThresholdLabel();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#btn_modify").click(function() {
|
||||||
|
$(this).blur();
|
||||||
|
var confirmMsg = isDetail ? '변경사항을 저장하시겠습니까?' : '새 어댑터를 등록하시겠습니까?';
|
||||||
|
showConfirm(confirmMsg, {
|
||||||
|
title: '저장 확인',
|
||||||
|
confirmText: '저장',
|
||||||
|
onConfirm: function() {
|
||||||
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
|
if (isDetail) {
|
||||||
|
postData.push({name: "cmd", value: "UPDATE"});
|
||||||
|
} else {
|
||||||
|
postData.push({name: "cmd", value: "INSERT"});
|
||||||
|
}
|
||||||
|
var isInsert = !isDetail;
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: postData,
|
||||||
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.saveMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '저장 완료',
|
||||||
|
onClose: isInsert ? function() { goNav(returnUrl); } : undefined
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_delete").click(function(){
|
|
||||||
|
|
||||||
if ( confirm( '<%=localeMessage.getString("common.confirmMsg")%>' ) != true ) return;
|
|
||||||
|
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
|
||||||
postData.push({ name: "cmd" , value:"DELETE"});
|
|
||||||
$.ajax({
|
|
||||||
type : "POST",
|
|
||||||
url:url,
|
|
||||||
data:postData,
|
|
||||||
success:function(args){
|
|
||||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
|
||||||
goNav(returnUrl);//LIST로 이동
|
|
||||||
|
|
||||||
},
|
$("#btn_delete").click(function() {
|
||||||
error:function(e){
|
$(this).blur();
|
||||||
alert(e.responseText);
|
var adapterName = $('input[name=desc]').val() || $('input[name=name]').val();
|
||||||
|
showConfirm('<strong>[' + adapterName + ']</strong> 어댑터를 삭제하시겠습니까?', {
|
||||||
|
type: 'error',
|
||||||
|
title: '삭제 확인',
|
||||||
|
confirmText: '삭제',
|
||||||
|
onConfirm: function() {
|
||||||
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
|
postData.push({name: "cmd", value: "DELETE"});
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: postData,
|
||||||
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.deleteMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '삭제 완료',
|
||||||
|
onClose: function() { goNav(returnUrl); }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_previous").click(function(){
|
|
||||||
goNav(returnUrl);//LIST로 이동
|
$("#btn_previous").click(function() {
|
||||||
|
goNav(returnUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonControl(isDetail);
|
buttonControl(isDetail);
|
||||||
titleControl(isDetail);
|
titleControl(isDetail);
|
||||||
|
|
||||||
|
$('.detail-tab').click(function() {
|
||||||
|
switchTab($(this).data('tab'));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isDetail) {
|
||||||
|
$('#tabStatus').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#btn_refresh_bucket').click(function() {
|
||||||
|
loadBucketStatus();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="right_box">
|
<div class="right_box">
|
||||||
<div class="content_top">
|
<div class="content_top">
|
||||||
<ul class="path">
|
<ul class="path">
|
||||||
<li><a href="#">${rmsMenuPath}</a></li>
|
<li><a href="#">${rmsMenuPath}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- end content_top -->
|
</div><!-- end content_top -->
|
||||||
<div class="content_middle">
|
<div class="content_middle" id="content_middle">
|
||||||
<div class="search_wrap">
|
<div class="search_wrap">
|
||||||
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %></button>
|
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %></button>
|
||||||
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></button>
|
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.save") %></button>
|
||||||
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
|
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
|
||||||
<%-- <img src="<c:url value="/img/btn_delete.png"/>" alt="" id="btn_delete" level="W" status="DETAIL"/> --%>
|
</div>
|
||||||
<%-- <img src="<c:url value="/img/btn_modify.png"/>" alt="" id="btn_modify" level="W" status="DETAIL,NEW" /> --%>
|
|
||||||
<%-- <img src="<c:url value="/img/btn_previous.png"/>" alt="" id="btn_previous" level="R" status="DETAIL,NEW"/> --%>
|
|
||||||
</div>
|
|
||||||
<div class="title"><%= localeMessage.getString("infAdpConMan.title") %><span class="tooltip" ><%= localeMessage.getString("infAdpConMan.title") %></span></div>
|
|
||||||
|
|
||||||
<table id="grid" ></table>
|
|
||||||
<div id="pager"></div>
|
|
||||||
|
|
||||||
<!-- detail -->
|
|
||||||
<form id="ajaxForm">
|
|
||||||
<table class="table_row" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.adaNm") %></th>
|
|
||||||
<td><input type="text" name="name" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.adaDes") %></th>
|
|
||||||
<td><input type="text" name="desc" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrPerSecond") %></th>
|
|
||||||
<td><input type="text" name="thresholdPerSecond" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thr") %></th>
|
|
||||||
<td><input type="text" name="threshold" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrTimeUnit") %></th>
|
|
||||||
<td>
|
|
||||||
<div class="select-style">
|
|
||||||
<select name="thresholdTimeUnit" />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
|
||||||
<td>
|
|
||||||
<div class="select-style">
|
|
||||||
<select name="useYn" />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div><!-- end content_middle -->
|
|
||||||
</div><!-- end right_box -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
|
<div class="title-wrap">
|
||||||
|
<div class="title"><%= localeMessage.getString("infAdpConMan.title") %><span class="tooltip"><%= localeMessage.getString("infAdpConMan.title") %></span></div>
|
||||||
|
<span id="modifiedAtInfo" class="modified-badge" style="display:none;">
|
||||||
|
<i class="material-icons">update</i><span id="modifiedAtText">-</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 탭 네비게이션 -->
|
||||||
|
<div class="detail-tabs">
|
||||||
|
<button type="button" class="detail-tab active" data-tab="config">
|
||||||
|
<i class="material-icons">settings</i> 설정
|
||||||
|
</button>
|
||||||
|
<button type="button" class="detail-tab" data-tab="status" id="tabStatus" style="display:none;">
|
||||||
|
<i class="material-icons">analytics</i> 현황
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 설정 탭 -->
|
||||||
|
<div class="tab-content active" id="tabConfigContent">
|
||||||
|
<form id="ajaxForm">
|
||||||
|
<span class="section-label">기본 정보</span>
|
||||||
|
<table class="form-table" cellspacing="0">
|
||||||
|
<colgroup>
|
||||||
|
<col style="width:150px" />
|
||||||
|
<col />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th><%= localeMessage.getString("infAdpConMan.adaNm") %></th>
|
||||||
|
<td><input type="text" name="name" style="width:300px; background-color: #ebebec;" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><%= localeMessage.getString("infAdpConMan.adaDes") %></th>
|
||||||
|
<td><input type="text" name="desc" style="width:300px; background-color: #ebebec;" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||||
|
<td>
|
||||||
|
<select name="useYn" class="styled-select"></select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<span class="section-label">유량제어 설정</span>
|
||||||
|
<table class="form-table" cellspacing="0">
|
||||||
|
<colgroup>
|
||||||
|
<col style="width:150px" />
|
||||||
|
<col />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">1초 단위의 순간 트래픽 제한입니다. 매 초마다 설정한 건수만큼 토큰이 리필되며, 초과 시 요청이 거부됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<input type="number" name="thresholdPerSecond" placeholder="0" />
|
||||||
|
<span class="unit">req/sec</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thr") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">장시간 누적 트래픽 제한입니다. 시간단위를 선택하면 해당 주기마다 토큰이 리필됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<select name="thresholdTimeUnit" class="styled-select"></select>
|
||||||
|
<input type="number" name="threshold" id="thresholdInput" placeholder="0" />
|
||||||
|
<span class="unit" id="thresholdUnit">requests</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div><!-- /tabConfigContent -->
|
||||||
|
|
||||||
|
<!-- 현황 탭 -->
|
||||||
|
<div class="tab-content" id="tabStatusContent">
|
||||||
|
<div class="bucket-status-wrap">
|
||||||
|
<div class="bucket-refresh-bar">
|
||||||
|
<span class="last-refresh">마지막 갱신: <span id="lastRefreshTime">-</span></span>
|
||||||
|
<button type="button" class="cssbtn" id="btn_refresh_bucket"><i class="material-icons">refresh</i> 새로고침</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 전체 합산 요약 -->
|
||||||
|
<div class="bucket-summary" id="bucketSummary" style="display:none;">
|
||||||
|
<div class="bucket-summary-title"><i class="material-icons">functions</i> 전체 인스턴스 합산</div>
|
||||||
|
<div class="bucket-summary-grid">
|
||||||
|
<div class="bucket-summary-item">
|
||||||
|
<span class="bucket-summary-label">활성 인스턴스</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryInstanceCount">0<span class="unit">개</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryPerSecondWrap">
|
||||||
|
<span class="bucket-summary-label">초당 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryPerSecond">0<span class="unit">req/sec</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryPerSecondCalc"></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryThresholdWrap">
|
||||||
|
<span class="bucket-summary-label">추가 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryThreshold">0<span class="unit">req</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryThresholdCalc"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bucket-server-list" id="bucketServerList">
|
||||||
|
<div class="bucket-empty-msg">
|
||||||
|
<i class="material-icons">hourglass_empty</i>
|
||||||
|
버킷 현황을 조회하려면 새로고침 버튼을 클릭하세요
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /tabStatusContent -->
|
||||||
|
|
||||||
|
</div><!-- end content_middle -->
|
||||||
|
</div><!-- end right_box -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
@@ -16,170 +16,514 @@
|
|||||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||||
|
|
||||||
<script language="javascript" >
|
<!-- 페이지 커스텀 스타일 (fragment) -->
|
||||||
|
<jsp:include page="fragments/inflowGroupDetailStyle.jsp"/>
|
||||||
|
|
||||||
|
<script language="javascript">
|
||||||
var url ='<c:url value="/onl/admin/inflow/inflowClientControlMan.json" />';
|
var url ='<c:url value="/onl/admin/inflow/inflowClientControlMan.json" />';
|
||||||
var url_view ='<c:url value="/onl/admin/inflow/inflowClientControlMan.view" />';
|
var url_view ='<c:url value="/onl/admin/inflow/inflowClientControlMan.view" />';
|
||||||
var isDetail = false;
|
var isDetail = false;
|
||||||
function init(url,key,callback){
|
|
||||||
$.ajax({
|
function updateThresholdLabel() {
|
||||||
type : "POST",
|
var timeUnit = $('select[name=thresholdTimeUnit]').val();
|
||||||
url:url,
|
var $input = $('#thresholdInput');
|
||||||
dataType:"json",
|
var $unit = $('#thresholdUnit');
|
||||||
data:{cmd: 'LIST_INIT_COMBO'},
|
|
||||||
success:function(json){
|
var unitLabels = {
|
||||||
|
'': '',
|
||||||
|
'SEC': 'req/sec',
|
||||||
|
'MIN': 'req/min',
|
||||||
|
'HOU': 'req/hour',
|
||||||
|
'DAY': 'req/day',
|
||||||
|
'MON': 'req/month'
|
||||||
|
};
|
||||||
|
|
||||||
|
$unit.text(unitLabels[timeUnit] || 'requests');
|
||||||
|
|
||||||
|
if (timeUnit === '') {
|
||||||
|
$input.prop('disabled', true).val('');
|
||||||
|
$unit.css('visibility', 'hidden');
|
||||||
|
} else {
|
||||||
|
$input.prop('disabled', false);
|
||||||
|
$unit.css('visibility', 'visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(url, key, callback) {
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
dataType: "json",
|
||||||
|
data: {cmd: 'LIST_INIT_COMBO'},
|
||||||
|
success: function(json) {
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
|
updateThresholdLabel();
|
||||||
if(typeof callback === 'function') {
|
|
||||||
callback(url,key);
|
if (typeof callback === 'function') {
|
||||||
|
callback(url, key);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function detail(url,key){
|
|
||||||
if (!isDetail)return;
|
function detail(url, key) {
|
||||||
$.ajax({
|
if (!isDetail) return;
|
||||||
type : "POST",
|
$.ajax({
|
||||||
url:url,
|
type: "POST",
|
||||||
dataType:"json",
|
url: url,
|
||||||
data:{cmd: 'DETAIL', name : key},
|
dataType: "json",
|
||||||
success:function(json){
|
data: {cmd: 'DETAIL', name: key},
|
||||||
|
success: function(json) {
|
||||||
var data = json;
|
var data = json;
|
||||||
$("input[name=name]").attr('readonly',true);
|
$("input[name=name]").attr('readonly', true);
|
||||||
$("input[name=desc]").attr('readonly',true);
|
$("input[name=desc]").attr('readonly', true);
|
||||||
|
|
||||||
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function(){
|
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function() {
|
||||||
var name = $(this).attr("name");
|
var name = $(this).attr("name");
|
||||||
var tag = $(this).prop("tagName").toLowerCase();
|
var tag = $(this).prop("tagName").toLowerCase();
|
||||||
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateThresholdLabel();
|
||||||
|
|
||||||
|
var modifiedAt = data.MODIFIEDAT || data.modifiedAt;
|
||||||
|
if (modifiedAt) {
|
||||||
|
$('#modifiedAtText').text(modifiedAt);
|
||||||
|
$('#modifiedAtInfo').show();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$(document).ready(function() {
|
|
||||||
var returnUrl = getReturnUrlForReturn();
|
function switchTab(tabName) {
|
||||||
var key ="${param.name}";
|
$('.detail-tab').removeClass('active');
|
||||||
if (key != "" && key !="null"){
|
$('.detail-tab[data-tab="' + tabName + '"]').addClass('active');
|
||||||
isDetail = true;
|
$('.tab-content').removeClass('active');
|
||||||
}
|
$('#tab' + tabName.charAt(0).toUpperCase() + tabName.slice(1) + 'Content').addClass('active');
|
||||||
init(url,key,detail);
|
|
||||||
|
if (tabName === 'status' && isDetail) {
|
||||||
|
loadBucketStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//버킷 현황 조회
|
||||||
|
function loadBucketStatus() {
|
||||||
|
|
||||||
|
$('#bucketServerList').html('<div class="bucket-empty-msg"><i class="material-icons">hourglass_empty</i>조회 중...</div>');
|
||||||
$("#btn_modify").click(function(){
|
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
$.ajax({
|
||||||
if (isDetail){
|
type: 'POST',
|
||||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
url: url,
|
||||||
}else{
|
data: { cmd: 'LIST_BUCKET_STATUS' },
|
||||||
postData.push({ name: "cmd" , value:"INSERT"});
|
dataType: 'json',
|
||||||
|
success: function(result) {
|
||||||
|
renderBucketStatus(result.servers || []);
|
||||||
|
$('#lastRefreshTime').text(formatDateTime(new Date()));
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
$('#bucketServerList').html('<div class="bucket-empty-msg bucket-error-msg"><i class="material-icons">error_outline</i>조회 실패: ' + e.statusText + '</div>');
|
||||||
}
|
}
|
||||||
$.ajax({
|
});
|
||||||
type : "POST",
|
}
|
||||||
url:url,
|
|
||||||
data:postData,
|
function formatDateTime(date) {
|
||||||
success:function(args){
|
var h = date.getHours();
|
||||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
var m = date.getMinutes();
|
||||||
goNav(returnUrl);//LIST로 이동
|
var s = date.getSeconds();
|
||||||
},
|
return (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
|
||||||
error:function(e){
|
}
|
||||||
alert(e.responseText);
|
|
||||||
|
function renderBucketStatus(servers) {
|
||||||
|
var container = $('#bucketServerList');
|
||||||
|
container.empty();
|
||||||
|
|
||||||
|
if (servers.length === 0) {
|
||||||
|
container.html('<div class="bucket-empty-msg"><i class="material-icons">dns</i>등록된 서버가 없습니다</div>');
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 전체 합산 계산
|
||||||
|
var onlineCount = 0;
|
||||||
|
var perSecondCapacity = 0;
|
||||||
|
var thresholdCapacity = 0;
|
||||||
|
var thresholdTimeUnit = '';
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
if (server.status === 'online' && server.data && server.data.buckets) {
|
||||||
|
onlineCount++;
|
||||||
|
$.each(server.data.buckets, function(j, bucket) {
|
||||||
|
if (bucket.type === 'perSecond') {
|
||||||
|
perSecondCapacity += bucket.capacity || 0;
|
||||||
|
} else if (bucket.type === 'threshold') {
|
||||||
|
thresholdCapacity += bucket.capacity || 0;
|
||||||
|
if (!thresholdTimeUnit) thresholdTimeUnit = bucket.timeUnit;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 요약 영역 표시
|
||||||
|
if (onlineCount > 0) {
|
||||||
|
$('#summaryInstanceCount').html(onlineCount + '<span class="unit">개</span>');
|
||||||
|
|
||||||
|
var configPerSecond = parseInt($('input[name=thresholdPerSecond]').val()) || 0;
|
||||||
|
var configThreshold = parseInt($('input[name=threshold]').val()) || 0;
|
||||||
|
|
||||||
|
if (configPerSecond > 0) {
|
||||||
|
$('#summaryPerSecond').html(formatNumber(perSecondCapacity) + '<span class="unit">req/sec</span>');
|
||||||
|
$('#summaryPerSecondCalc').text(formatNumber(configPerSecond) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryPerSecondWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryPerSecondWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configThreshold > 0) {
|
||||||
|
var timeUnitLabel = getTimeUnitText(thresholdTimeUnit);
|
||||||
|
$('#summaryThreshold').html(formatNumber(thresholdCapacity) + '<span class="unit">req/' + timeUnitLabel + '</span>');
|
||||||
|
$('#summaryThresholdCalc').text(formatNumber(configThreshold) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryThresholdWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryThresholdWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 활성 API 정보 표시
|
||||||
|
var activeApis = interfaceList.filter(function(item) {
|
||||||
|
return item.useYn === '1' || item.useYn === undefined;
|
||||||
|
});
|
||||||
|
$('#summaryActiveApi').html(activeApis.length + '<span class="unit">개</span>');
|
||||||
|
if (activeApis.length > 0) {
|
||||||
|
var apiIds = activeApis.map(function(item) { return item.interfaceId; });
|
||||||
|
var displayText = apiIds.length <= 3 ? apiIds.join(', ') : apiIds.slice(0, 3).join(', ') + ' 외 ' + (apiIds.length - 3) + '개';
|
||||||
|
$('#summaryActiveApiList').text(displayText);
|
||||||
|
} else {
|
||||||
|
$('#summaryActiveApiList').text('');
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#bucketSummary').show();
|
||||||
|
} else {
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
var statusClass = server.status || 'offline';
|
||||||
|
var statusText = { online: '정상', offline: '오프라인', no_data: '데이터 없음' };
|
||||||
|
|
||||||
|
var card = '<div class="bucket-server-card">' +
|
||||||
|
'<div class="bucket-server-header">' +
|
||||||
|
'<div>' +
|
||||||
|
'<span class="bucket-server-name">' + server.serverName + '</span> ' +
|
||||||
|
'<span class="bucket-server-ip">' + server.serverIp + ':' + server.serverPort + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<span class="bucket-server-status ' + statusClass + '">' + (statusText[statusClass] || statusClass) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-server-body">' + renderBucketBody(server) + '</div>' +
|
||||||
|
'</div>';
|
||||||
|
container.append(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBucketBody(server) {
|
||||||
|
if (server.status === 'offline') {
|
||||||
|
return '<div class="bucket-error-msg">' + (server.message || '서버 연결 실패') + '</div>';
|
||||||
|
}
|
||||||
|
if (server.status === 'no_data') {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;"><i class="material-icons" style="font-size:18px;">info_outline</i> ' + (server.message || '그룹이 로드되지 않음') + '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = server.data;
|
||||||
|
if (!data || !data.buckets || data.buckets.length === 0) {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;">버킷 정보 없음</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '<div class="bucket-info-grid">';
|
||||||
|
$.each(data.buckets, function(j, bucket) {
|
||||||
|
var typeLabel = bucket.type === 'perSecond' ? '초당 임계치' : '추가 임계치';
|
||||||
|
var timeUnitText = getTimeUnitText(bucket.timeUnit);
|
||||||
|
var usagePercent = bucket.usagePercent || 0;
|
||||||
|
var progressClass = usagePercent < 50 ? 'low' : (usagePercent < 80 ? 'medium' : 'high');
|
||||||
|
|
||||||
|
html += '<div class="bucket-card">' +
|
||||||
|
'<div class="bucket-card-header">' +
|
||||||
|
'<span class="bucket-type">' + typeLabel + '</span>' +
|
||||||
|
'<span class="bucket-type-badge">' + timeUnitText + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-progress-wrap">' +
|
||||||
|
'<div class="bucket-progress-bar">' +
|
||||||
|
'<div class="bucket-progress-fill ' + progressClass + '" style="width:' + usagePercent + '%"></div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-stats">' +
|
||||||
|
'<span>사용량: <span class="bucket-stats-value">' + usagePercent.toFixed(1) + '%</span></span>' +
|
||||||
|
'<span>잔여: <span class="bucket-stats-value">' + formatNumber(bucket.availableTokens) + '</span> / ' + formatNumber(bucket.capacity) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimeUnitText(timeUnit) {
|
||||||
|
var units = {SEC: '초', MIN: '분', HOU: '시간', DAY: '일', MON: '월'};
|
||||||
|
return units[timeUnit] || timeUnit || '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(num) {
|
||||||
|
if (num === undefined || num === null) return '-';
|
||||||
|
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
var returnUrl = getReturnUrlForReturn();
|
||||||
|
var key = "${param.name}";
|
||||||
|
if (key != "" && key != "null") {
|
||||||
|
isDetail = true;
|
||||||
|
}
|
||||||
|
init(url, key, detail);
|
||||||
|
|
||||||
|
$("select[name=thresholdTimeUnit]").on('change', function() {
|
||||||
|
updateThresholdLabel();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#btn_modify").click(function() {
|
||||||
|
$(this).blur();
|
||||||
|
var confirmMsg = isDetail ? '변경사항을 저장하시겠습니까?' : '새 클라이언트를 등록하시겠습니까?';
|
||||||
|
showConfirm(confirmMsg, {
|
||||||
|
title: '저장 확인',
|
||||||
|
confirmText: '저장',
|
||||||
|
onConfirm: function() {
|
||||||
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
|
if (isDetail) {
|
||||||
|
postData.push({name: "cmd", value: "UPDATE"});
|
||||||
|
} else {
|
||||||
|
postData.push({name: "cmd", value: "INSERT"});
|
||||||
|
}
|
||||||
|
var isInsert = !isDetail;
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: postData,
|
||||||
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.saveMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '저장 완료',
|
||||||
|
onClose: isInsert ? function() { goNav(returnUrl); } : undefined
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_delete").click(function(){
|
|
||||||
|
|
||||||
if ( confirm( '<%=localeMessage.getString("common.confirmMsg")%>' ) != true ) return;
|
|
||||||
|
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
|
||||||
postData.push({ name: "cmd" , value:"DELETE"});
|
|
||||||
$.ajax({
|
|
||||||
type : "POST",
|
|
||||||
url:url,
|
|
||||||
data:postData,
|
|
||||||
success:function(args){
|
|
||||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
|
||||||
goNav(returnUrl);//LIST로 이동
|
|
||||||
|
|
||||||
},
|
$("#btn_delete").click(function() {
|
||||||
error:function(e){
|
$(this).blur();
|
||||||
alert(e.responseText);
|
var clientName = $('input[name=desc]').val() || $('input[name=name]').val();
|
||||||
|
showConfirm('<strong>[' + clientName + ']</strong> 클라이언트를 삭제하시겠습니까?', {
|
||||||
|
type: 'error',
|
||||||
|
title: '삭제 확인',
|
||||||
|
confirmText: '삭제',
|
||||||
|
onConfirm: function() {
|
||||||
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
|
postData.push({name: "cmd", value: "DELETE"});
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: postData,
|
||||||
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.deleteMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '삭제 완료',
|
||||||
|
onClose: function() { goNav(returnUrl); }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_previous").click(function(){
|
|
||||||
goNav(returnUrl);//LIST로 이동
|
$("#btn_previous").click(function() {
|
||||||
|
goNav(returnUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonControl(isDetail);
|
buttonControl(isDetail);
|
||||||
titleControl(isDetail);
|
titleControl(isDetail);
|
||||||
|
|
||||||
|
$('.detail-tab').click(function() {
|
||||||
|
switchTab($(this).data('tab'));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isDetail) {
|
||||||
|
$('#tabStatus').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#btn_refresh_bucket').click(function() {
|
||||||
|
loadBucketStatus();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).keydown(function(e) {
|
||||||
|
if (e.keyCode === 27) {
|
||||||
|
// ESC 키 처리 (필요시 모달 닫기 등)
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="right_box">
|
<div class="right_box">
|
||||||
<div class="content_top">
|
<div class="content_top">
|
||||||
<ul class="path">
|
<ul class="path">
|
||||||
<li><a href="#">${rmsMenuPath}</a></li>
|
<li><a href="#">${rmsMenuPath}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- end content_top -->
|
</div><!-- end content_top -->
|
||||||
<div class="content_middle">
|
<div class="content_middle" id="content_middle">
|
||||||
<div class="search_wrap">
|
<div class="search_wrap">
|
||||||
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %></button>
|
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %></button>
|
||||||
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></button>
|
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.save") %></button>
|
||||||
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
|
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">클라이언트 유량제어 <span class="tooltip" >클라이언트 유량제어 </span></div>
|
|
||||||
|
|
||||||
<table id="grid" ></table>
|
|
||||||
<div id="pager"></div>
|
|
||||||
|
|
||||||
<!-- detail -->
|
|
||||||
<form id="ajaxForm">
|
|
||||||
<table class="table_row" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;">클라이언트 ID</th>
|
|
||||||
<td><input type="text" name="name" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;">클라이언트명</th>
|
|
||||||
<td><input type="text" name="desc" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrPerSecond") %></th>
|
|
||||||
<td><input type="text" name="thresholdPerSecond" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thr") %></th>
|
|
||||||
<td><input type="text" name="threshold" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrTimeUnit") %></th>
|
|
||||||
<td>
|
|
||||||
<div class="select-style">
|
|
||||||
<select name="thresholdTimeUnit" />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
|
||||||
<td>
|
|
||||||
<div class="select-style">
|
|
||||||
<select name="useYn" />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div><!-- end content_middle -->
|
|
||||||
</div><!-- end right_box -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
|
<div class="title-wrap">
|
||||||
|
<div class="title">클라이언트 유량제어<span class="tooltip">클라이언트 유량제어</span></div>
|
||||||
|
<span id="modifiedAtInfo" class="modified-badge" style="display:none;">
|
||||||
|
<i class="material-icons">update</i><span id="modifiedAtText">-</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 탭 네비게이션 -->
|
||||||
|
<div class="detail-tabs">
|
||||||
|
<button type="button" class="detail-tab active" data-tab="config">
|
||||||
|
<i class="material-icons">settings</i> 설정
|
||||||
|
</button>
|
||||||
|
<button type="button" class="detail-tab" data-tab="status" id="tabStatus" style="display:none;">
|
||||||
|
<i class="material-icons">analytics</i> 현황
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 설정 탭 -->
|
||||||
|
<div class="tab-content active" id="tabConfigContent">
|
||||||
|
<form id="ajaxForm">
|
||||||
|
<span class="section-label">기본 정보</span>
|
||||||
|
<table class="form-table" cellspacing="0">
|
||||||
|
<colgroup>
|
||||||
|
<col style="width:150px" />
|
||||||
|
<col />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>클라이언트 ID</th>
|
||||||
|
<td><input type="text" name="name" style="width:300px; background-color: #ebebec;" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>클라이언트명</th>
|
||||||
|
<td><input type="text" name="desc" style="width:300px; background-color: #ebebec;" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||||
|
<td>
|
||||||
|
<select name="useYn" class="styled-select"></select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<span class="section-label">유량제어 설정</span>
|
||||||
|
<table class="form-table" cellspacing="0">
|
||||||
|
<colgroup>
|
||||||
|
<col style="width:150px" />
|
||||||
|
<col />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">1초 단위의 순간 트래픽 제한입니다. 매 초마다 설정한 건수만큼 토큰이 리필되며, 초과 시 요청이 거부됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<input type="number" name="thresholdPerSecond" placeholder="0" />
|
||||||
|
<span class="unit">req/sec</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thr") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">장시간 누적 트래픽 제한입니다. 시간단위를 선택하면 해당 주기마다 토큰이 리필됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<select name="thresholdTimeUnit" class="styled-select"></select>
|
||||||
|
<input type="number" name="threshold" id="thresholdInput" placeholder="0" />
|
||||||
|
<span class="unit" id="thresholdUnit">requests</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div><!-- /tabConfigContent -->
|
||||||
|
|
||||||
|
<!-- 현황 탭 -->
|
||||||
|
<div class="tab-content" id="tabStatusContent">
|
||||||
|
<div class="bucket-status-wrap">
|
||||||
|
<div class="bucket-refresh-bar">
|
||||||
|
<span class="last-refresh">마지막 갱신: <span id="lastRefreshTime">-</span></span>
|
||||||
|
<button type="button" class="cssbtn" id="btn_refresh_bucket"><i class="material-icons">refresh</i> 새로고침</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 전체 합산 요약 -->
|
||||||
|
<div class="bucket-summary" id="bucketSummary" style="display:none;">
|
||||||
|
<div class="bucket-summary-title"><i class="material-icons">functions</i> 전체 인스턴스 합산</div>
|
||||||
|
<div class="bucket-summary-grid">
|
||||||
|
<div class="bucket-summary-item">
|
||||||
|
<span class="bucket-summary-label">활성 인스턴스</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryInstanceCount">0<span class="unit">개</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryPerSecondWrap">
|
||||||
|
<span class="bucket-summary-label">초당 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryPerSecond">0<span class="unit">req/sec</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryPerSecondCalc"></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryThresholdWrap">
|
||||||
|
<span class="bucket-summary-label">추가 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryThreshold">0<span class="unit">req</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryThresholdCalc"></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item">
|
||||||
|
<span class="bucket-summary-label">적용 API</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryActiveApi">0<span class="unit">개</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryActiveApiList"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bucket-server-list" id="bucketServerList">
|
||||||
|
<div class="bucket-empty-msg">
|
||||||
|
<i class="material-icons">hourglass_empty</i>
|
||||||
|
버킷 현황을 조회하려면 새로고침 버튼을 클릭하세요
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /tabStatusContent -->
|
||||||
|
|
||||||
|
</div><!-- end content_middle -->
|
||||||
|
</div><!-- end right_box -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
@@ -16,173 +16,486 @@
|
|||||||
<jsp:include page="/jsp/common/include/css.jsp"/>
|
<jsp:include page="/jsp/common/include/css.jsp"/>
|
||||||
<jsp:include page="/jsp/common/include/script.jsp"/>
|
<jsp:include page="/jsp/common/include/script.jsp"/>
|
||||||
|
|
||||||
<script language="javascript" >
|
<!-- 페이지 커스텀 스타일 (fragment) -->
|
||||||
|
<jsp:include page="fragments/inflowGroupDetailStyle.jsp"/>
|
||||||
|
|
||||||
|
<script language="javascript">
|
||||||
var url ='<c:url value="/onl/admin/inflow/inflowInterfaceControlMan.json" />';
|
var url ='<c:url value="/onl/admin/inflow/inflowInterfaceControlMan.json" />';
|
||||||
var url_view ='<c:url value="/onl/admin/inflow/inflowInterfaceControlMan.view" />';
|
var url_view ='<c:url value="/onl/admin/inflow/inflowInterfaceControlMan.view" />';
|
||||||
var isDetail = false;
|
var isDetail = false;
|
||||||
function init(url,key,callback){
|
|
||||||
$.ajax({
|
function updateThresholdLabel() {
|
||||||
type : "POST",
|
var timeUnit = $('select[name=thresholdTimeUnit]').val();
|
||||||
url:url,
|
var $input = $('#thresholdInput');
|
||||||
dataType:"json",
|
var $unit = $('#thresholdUnit');
|
||||||
data:{cmd: 'LIST_INIT_COMBO'},
|
|
||||||
success:function(json){
|
var unitLabels = {
|
||||||
|
'': '',
|
||||||
|
'SEC': 'req/sec',
|
||||||
|
'MIN': 'req/min',
|
||||||
|
'HOU': 'req/hour',
|
||||||
|
'DAY': 'req/day',
|
||||||
|
'MON': 'req/month'
|
||||||
|
};
|
||||||
|
|
||||||
|
$unit.text(unitLabels[timeUnit] || 'requests');
|
||||||
|
|
||||||
|
if (timeUnit === '') {
|
||||||
|
$input.prop('disabled', true).val('');
|
||||||
|
$unit.css('visibility', 'hidden');
|
||||||
|
} else {
|
||||||
|
$input.prop('disabled', false);
|
||||||
|
$unit.css('visibility', 'visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init(url, key, callback) {
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
dataType: "json",
|
||||||
|
data: {cmd: 'LIST_INIT_COMBO'},
|
||||||
|
success: function(json) {
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=useYn]")).setData(json.useYnRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
new makeOptions("CODE","NAME").setObj($("select[name=thresholdTimeUnit]")).setNoValueInclude(true).setData(json.timeUnitRows).setFormat(codeName3OptionFormat).rendering();
|
||||||
|
updateThresholdLabel();
|
||||||
if(typeof callback === 'function') {
|
|
||||||
callback(url,key);
|
if (typeof callback === 'function') {
|
||||||
|
callback(url, key);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function detail(url,key){
|
|
||||||
if (!isDetail)return;
|
function detail(url, key) {
|
||||||
$.ajax({
|
if (!isDetail) return;
|
||||||
type : "POST",
|
$.ajax({
|
||||||
url:url,
|
type: "POST",
|
||||||
dataType:"json",
|
url: url,
|
||||||
data:{cmd: 'DETAIL', name : key},
|
dataType: "json",
|
||||||
success:function(json){
|
data: {cmd: 'DETAIL', name: key},
|
||||||
|
success: function(json) {
|
||||||
var data = json;
|
var data = json;
|
||||||
$("input[name=name]").attr('readonly',true);
|
$("input[name=name]").attr('readonly', true);
|
||||||
$("input[name=desc]").attr('readonly',true);
|
$("input[name=desc]").attr('readonly', true);
|
||||||
|
|
||||||
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function(){
|
$("#ajaxForm input[type!=radio],#ajaxForm select,#ajaxForm textarea").each(function() {
|
||||||
var name = $(this).attr("name");
|
var name = $(this).attr("name");
|
||||||
var tag = $(this).prop("tagName").toLowerCase();
|
var tag = $(this).prop("tagName").toLowerCase();
|
||||||
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
$(tag+"[name="+name+"]").val(data[name.toUpperCase()]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateThresholdLabel();
|
||||||
|
|
||||||
|
var modifiedAt = data.MODIFIEDAT || data.modifiedAt;
|
||||||
|
if (modifiedAt) {
|
||||||
|
$('#modifiedAtText').text(modifiedAt);
|
||||||
|
$('#modifiedAtInfo').show();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error:function(e){
|
error: function(e) {
|
||||||
alert(e.responseText);
|
alert(e.responseText);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$(document).ready(function() {
|
|
||||||
var returnUrl = getReturnUrlForReturn();
|
function switchTab(tabName) {
|
||||||
var key ="${param.name}";
|
$('.detail-tab').removeClass('active');
|
||||||
if (key != "" && key !="null"){
|
$('.detail-tab[data-tab="' + tabName + '"]').addClass('active');
|
||||||
isDetail = true;
|
$('.tab-content').removeClass('active');
|
||||||
}
|
$('#tab' + tabName.charAt(0).toUpperCase() + tabName.slice(1) + 'Content').addClass('active');
|
||||||
init(url,key,detail);
|
|
||||||
|
if (tabName === 'status' && isDetail) {
|
||||||
|
loadBucketStatus();
|
||||||
$("#btn_modify").click(function(){
|
}
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
}
|
||||||
if (isDetail){
|
|
||||||
postData.push({ name: "cmd" , value:"UPDATE"});
|
function loadBucketStatus() {
|
||||||
}else{
|
$('#bucketServerList').html('<div class="bucket-empty-msg"><i class="material-icons">hourglass_empty</i>조회 중...</div>');
|
||||||
postData.push({ name: "cmd" , value:"INSERT"});
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: url,
|
||||||
|
data: { cmd: 'LIST_BUCKET_STATUS' },
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(result) {
|
||||||
|
renderBucketStatus(result.servers || []);
|
||||||
|
$('#lastRefreshTime').text(formatDateTime(new Date()));
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
$('#bucketServerList').html('<div class="bucket-empty-msg bucket-error-msg"><i class="material-icons">error_outline</i>조회 실패: ' + e.statusText + '</div>');
|
||||||
}
|
}
|
||||||
$.ajax({
|
});
|
||||||
type : "POST",
|
}
|
||||||
url:url,
|
|
||||||
data:postData,
|
function formatDateTime(date) {
|
||||||
success:function(args){
|
var h = date.getHours();
|
||||||
alert("<%= localeMessage.getString("common.saveMsg") %>");
|
var m = date.getMinutes();
|
||||||
goNav(returnUrl);//LIST로 이동
|
var s = date.getSeconds();
|
||||||
},
|
return (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
|
||||||
error:function(e){
|
}
|
||||||
alert(e.responseText);
|
|
||||||
|
function renderBucketStatus(servers) {
|
||||||
|
var container = $('#bucketServerList');
|
||||||
|
container.empty();
|
||||||
|
|
||||||
|
if (servers.length === 0) {
|
||||||
|
container.html('<div class="bucket-empty-msg"><i class="material-icons">dns</i>등록된 서버가 없습니다</div>');
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var onlineCount = 0;
|
||||||
|
var perSecondCapacity = 0;
|
||||||
|
var thresholdCapacity = 0;
|
||||||
|
var thresholdTimeUnit = '';
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
if (server.status === 'online' && server.data && server.data.buckets) {
|
||||||
|
onlineCount++;
|
||||||
|
$.each(server.data.buckets, function(j, bucket) {
|
||||||
|
if (bucket.type === 'perSecond') {
|
||||||
|
perSecondCapacity += bucket.capacity || 0;
|
||||||
|
} else if (bucket.type === 'threshold') {
|
||||||
|
thresholdCapacity += bucket.capacity || 0;
|
||||||
|
if (!thresholdTimeUnit) thresholdTimeUnit = bucket.timeUnit;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (onlineCount > 0) {
|
||||||
|
$('#summaryInstanceCount').html(onlineCount + '<span class="unit">개</span>');
|
||||||
|
|
||||||
|
var configPerSecond = parseInt($('input[name=thresholdPerSecond]').val()) || 0;
|
||||||
|
var configThreshold = parseInt($('input[name=threshold]').val()) || 0;
|
||||||
|
|
||||||
|
if (configPerSecond > 0) {
|
||||||
|
$('#summaryPerSecond').html(formatNumber(perSecondCapacity) + '<span class="unit">req/sec</span>');
|
||||||
|
$('#summaryPerSecondCalc').text(formatNumber(configPerSecond) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryPerSecondWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryPerSecondWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configThreshold > 0) {
|
||||||
|
var timeUnitLabel = getTimeUnitText(thresholdTimeUnit);
|
||||||
|
$('#summaryThreshold').html(formatNumber(thresholdCapacity) + '<span class="unit">req/' + timeUnitLabel + '</span>');
|
||||||
|
$('#summaryThresholdCalc').text(formatNumber(configThreshold) + ' × ' + onlineCount + ' 인스턴스');
|
||||||
|
$('#summaryThresholdWrap').show();
|
||||||
|
} else {
|
||||||
|
$('#summaryThresholdWrap').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#bucketSummary').show();
|
||||||
|
} else {
|
||||||
|
$('#bucketSummary').hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$.each(servers, function(i, server) {
|
||||||
|
var statusClass = server.status || 'offline';
|
||||||
|
var statusText = { online: '정상', offline: '오프라인', no_data: '데이터 없음' };
|
||||||
|
|
||||||
|
var card = '<div class="bucket-server-card">' +
|
||||||
|
'<div class="bucket-server-header">' +
|
||||||
|
'<div>' +
|
||||||
|
'<span class="bucket-server-name">' + server.serverName + '</span> ' +
|
||||||
|
'<span class="bucket-server-ip">' + server.serverIp + ':' + server.serverPort + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<span class="bucket-server-status ' + statusClass + '">' + (statusText[statusClass] || statusClass) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-server-body">' + renderBucketBody(server) + '</div>' +
|
||||||
|
'</div>';
|
||||||
|
container.append(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderBucketBody(server) {
|
||||||
|
if (server.status === 'offline') {
|
||||||
|
return '<div class="bucket-error-msg">' + (server.message || '서버 연결 실패') + '</div>';
|
||||||
|
}
|
||||||
|
if (server.status === 'no_data') {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;"><i class="material-icons" style="font-size:18px;">info_outline</i> ' + (server.message || '인터페이스가 로드되지 않음') + '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = server.data;
|
||||||
|
if (!data || !data.buckets || data.buckets.length === 0) {
|
||||||
|
return '<div class="bucket-empty-msg" style="padding:10px;">버킷 정보 없음</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var html = '<div class="bucket-info-grid">';
|
||||||
|
$.each(data.buckets, function(j, bucket) {
|
||||||
|
var typeLabel = bucket.type === 'perSecond' ? '초당 임계치' : '추가 임계치';
|
||||||
|
var timeUnitText = getTimeUnitText(bucket.timeUnit);
|
||||||
|
var usagePercent = bucket.usagePercent || 0;
|
||||||
|
var progressClass = usagePercent < 50 ? 'low' : (usagePercent < 80 ? 'medium' : 'high');
|
||||||
|
|
||||||
|
html += '<div class="bucket-card">' +
|
||||||
|
'<div class="bucket-card-header">' +
|
||||||
|
'<span class="bucket-type">' + typeLabel + '</span>' +
|
||||||
|
'<span class="bucket-type-badge">' + timeUnitText + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-progress-wrap">' +
|
||||||
|
'<div class="bucket-progress-bar">' +
|
||||||
|
'<div class="bucket-progress-fill ' + progressClass + '" style="width:' + usagePercent + '%"></div>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>' +
|
||||||
|
'<div class="bucket-stats">' +
|
||||||
|
'<span>사용량: <span class="bucket-stats-value">' + usagePercent.toFixed(1) + '%</span></span>' +
|
||||||
|
'<span>잔여: <span class="bucket-stats-value">' + formatNumber(bucket.availableTokens) + '</span> / ' + formatNumber(bucket.capacity) + '</span>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTimeUnitText(timeUnit) {
|
||||||
|
var units = {SEC: '초', MIN: '분', HOU: '시간', DAY: '일', MON: '월'};
|
||||||
|
return units[timeUnit] || timeUnit || '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(num) {
|
||||||
|
if (num === undefined || num === null) return '-';
|
||||||
|
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
var returnUrl = getReturnUrlForReturn();
|
||||||
|
var key = "${param.name}";
|
||||||
|
if (key != "" && key != "null") {
|
||||||
|
isDetail = true;
|
||||||
|
}
|
||||||
|
init(url, key, detail);
|
||||||
|
|
||||||
|
$("select[name=thresholdTimeUnit]").on('change', function() {
|
||||||
|
updateThresholdLabel();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#btn_modify").click(function() {
|
||||||
|
$(this).blur();
|
||||||
|
var confirmMsg = isDetail ? '변경사항을 저장하시겠습니까?' : '새 인터페이스를 등록하시겠습니까?';
|
||||||
|
showConfirm(confirmMsg, {
|
||||||
|
title: '저장 확인',
|
||||||
|
confirmText: '저장',
|
||||||
|
onConfirm: function() {
|
||||||
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
|
if (isDetail) {
|
||||||
|
postData.push({name: "cmd", value: "UPDATE"});
|
||||||
|
} else {
|
||||||
|
postData.push({name: "cmd", value: "INSERT"});
|
||||||
|
}
|
||||||
|
var isInsert = !isDetail;
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: postData,
|
||||||
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.saveMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '저장 완료',
|
||||||
|
onClose: isInsert ? function() { goNav(returnUrl); } : undefined
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_delete").click(function(){
|
|
||||||
|
|
||||||
if ( confirm( '<%=localeMessage.getString("common.confirmMsg")%>' ) != true ) return;
|
|
||||||
|
|
||||||
var postData = $('#ajaxForm').serializeArray();
|
|
||||||
postData.push({ name: "cmd" , value:"DELETE"});
|
|
||||||
$.ajax({
|
|
||||||
type : "POST",
|
|
||||||
url:url,
|
|
||||||
data:postData,
|
|
||||||
success:function(args){
|
|
||||||
alert("<%= localeMessage.getString("common.deleteMsg") %>");
|
|
||||||
goNav(returnUrl);//LIST로 이동
|
|
||||||
|
|
||||||
},
|
$("#btn_delete").click(function() {
|
||||||
error:function(e){
|
$(this).blur();
|
||||||
alert(e.responseText);
|
var ifName = $('input[name=desc]').val() || $('input[name=name]').val();
|
||||||
|
showConfirm('<strong>[' + ifName + ']</strong> 인터페이스를 삭제하시겠습니까?', {
|
||||||
|
type: 'error',
|
||||||
|
title: '삭제 확인',
|
||||||
|
confirmText: '삭제',
|
||||||
|
onConfirm: function() {
|
||||||
|
var postData = $('#ajaxForm').serializeArray();
|
||||||
|
postData.push({name: "cmd", value: "DELETE"});
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: postData,
|
||||||
|
success: function(args) {
|
||||||
|
showAlert("<%= localeMessage.getString("common.deleteMsg") %>", {
|
||||||
|
type: 'success',
|
||||||
|
title: '삭제 완료',
|
||||||
|
onClose: function() { goNav(returnUrl); }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(e) {
|
||||||
|
showAlert(e.responseText, {type: 'error'});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
$("#btn_previous").click(function(){
|
|
||||||
goNav(returnUrl);//LIST로 이동
|
$("#btn_previous").click(function() {
|
||||||
|
goNav(returnUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonControl(isDetail);
|
buttonControl(isDetail);
|
||||||
titleControl(isDetail);
|
titleControl(isDetail);
|
||||||
|
|
||||||
|
$('.detail-tab').click(function() {
|
||||||
|
switchTab($(this).data('tab'));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isDetail) {
|
||||||
|
$('#tabStatus').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#btn_refresh_bucket').click(function() {
|
||||||
|
loadBucketStatus();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="right_box">
|
<div class="right_box">
|
||||||
<div class="content_top">
|
<div class="content_top">
|
||||||
<ul class="path">
|
<ul class="path">
|
||||||
<li><a href="#">${rmsMenuPath}</a></li>
|
<li><a href="#">${rmsMenuPath}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- end content_top -->
|
</div><!-- end content_top -->
|
||||||
<div class="content_middle">
|
<div class="content_middle" id="content_middle">
|
||||||
<div class="search_wrap">
|
<div class="search_wrap">
|
||||||
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %></button>
|
<button type="button" class="cssbtn" id="btn_delete" level="W" status="DETAIL"><i class="material-icons">delete</i> <%= localeMessage.getString("button.delete") %></button>
|
||||||
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></button>
|
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.save") %></button>
|
||||||
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
|
<button type="button" class="cssbtn" id="btn_previous" level="R" status="DETAIL,NEW"><i class="material-icons">arrow_back</i> <%= localeMessage.getString("button.previous") %></button>
|
||||||
<%-- <img src="<c:url value="/img/btn_delete.png"/>" alt="" id="btn_delete" level="W" status="DETAIL"/> --%>
|
</div>
|
||||||
<%-- <img src="<c:url value="/img/btn_modify.png"/>" alt="" id="btn_modify" level="W" status="DETAIL,NEW" /> --%>
|
|
||||||
<%-- <img src="<c:url value="/img/btn_previous.png"/>" alt="" id="btn_previous" level="R" status="DETAIL,NEW"/> --%>
|
|
||||||
</div>
|
|
||||||
<div class="title"><%= localeMessage.getString("infIfConMan.title") %><span class="tooltip" ><%= localeMessage.getString("infIfConMan.title") %></span></div>
|
|
||||||
|
|
||||||
<table id="grid" ></table>
|
|
||||||
<div id="pager"></div>
|
|
||||||
|
|
||||||
<!-- detail -->
|
|
||||||
<form id="ajaxForm">
|
|
||||||
<table class="table_row" cellspacing="0">
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infIfConMan.ifNm") %></th>
|
|
||||||
<td><input type="text" name="name" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infIfConMan.ifDesc") %></th>
|
|
||||||
<td><input type="text" name="desc" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrPerSecond") %></th>
|
|
||||||
<td><input type="text" name="thresholdPerSecond" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thr") %></th>
|
|
||||||
<td><input type="text" name="threshold" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.thrTimeUnit") %></th>
|
|
||||||
<td>
|
|
||||||
<div class="select-style">
|
|
||||||
<select name="thresholdTimeUnit" />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th style="width:20%;"><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
|
||||||
<td>
|
|
||||||
<div class="select-style">
|
|
||||||
<select name="useYn" />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div><!-- end content_middle -->
|
|
||||||
</div><!-- end right_box -->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
|
<div class="title-wrap">
|
||||||
|
<div class="title"><%= localeMessage.getString("infIfConMan.title") %><span class="tooltip"><%= localeMessage.getString("infIfConMan.title") %></span></div>
|
||||||
|
<span id="modifiedAtInfo" class="modified-badge" style="display:none;">
|
||||||
|
<i class="material-icons">update</i><span id="modifiedAtText">-</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 탭 네비게이션 -->
|
||||||
|
<div class="detail-tabs">
|
||||||
|
<button type="button" class="detail-tab active" data-tab="config">
|
||||||
|
<i class="material-icons">settings</i> 설정
|
||||||
|
</button>
|
||||||
|
<button type="button" class="detail-tab" data-tab="status" id="tabStatus" style="display:none;">
|
||||||
|
<i class="material-icons">analytics</i> 현황
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 설정 탭 -->
|
||||||
|
<div class="tab-content active" id="tabConfigContent">
|
||||||
|
<form id="ajaxForm">
|
||||||
|
<span class="section-label">기본 정보</span>
|
||||||
|
<table class="form-table" cellspacing="0">
|
||||||
|
<colgroup>
|
||||||
|
<col style="width:150px" />
|
||||||
|
<col />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th><%= localeMessage.getString("infIfConMan.ifNm") %></th>
|
||||||
|
<td><input type="text" name="name" style="width:300px;background-color: #ebebec;" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><%= localeMessage.getString("infIfConMan.ifDesc") %></th>
|
||||||
|
<td><input type="text" name="desc" style="width:300px;background-color: #ebebec;" /></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th><%= localeMessage.getString("infAdpConMan.useYn") %></th>
|
||||||
|
<td>
|
||||||
|
<select name="useYn" class="styled-select"></select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<span class="section-label">유량제어 설정</span>
|
||||||
|
<table class="form-table" cellspacing="0">
|
||||||
|
<colgroup>
|
||||||
|
<col style="width:150px" />
|
||||||
|
<col />
|
||||||
|
</colgroup>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thrPerSecond") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">1초 단위의 순간 트래픽 제한입니다. 매 초마다 설정한 건수만큼 토큰이 리필되며, 초과 시 요청이 거부됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<input type="number" name="thresholdPerSecond" placeholder="0" />
|
||||||
|
<span class="unit">req/sec</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
<%= localeMessage.getString("infAdpConMan.thr") %>
|
||||||
|
<span class="hint-icon"><i class="material-icons">help_outline</i>
|
||||||
|
<span class="hint-bubble">장시간 누적 트래픽 제한입니다. 시간단위를 선택하면 해당 주기마다 토큰이 리필됩니다.</span>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<div class="threshold-group">
|
||||||
|
<select name="thresholdTimeUnit" class="styled-select"></select>
|
||||||
|
<input type="number" name="threshold" id="thresholdInput" placeholder="0" />
|
||||||
|
<span class="unit" id="thresholdUnit">requests</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div><!-- /tabConfigContent -->
|
||||||
|
|
||||||
|
<!-- 현황 탭 -->
|
||||||
|
<div class="tab-content" id="tabStatusContent">
|
||||||
|
<div class="bucket-status-wrap">
|
||||||
|
<div class="bucket-refresh-bar">
|
||||||
|
<span class="last-refresh">마지막 갱신: <span id="lastRefreshTime">-</span></span>
|
||||||
|
<button type="button" class="cssbtn" id="btn_refresh_bucket"><i class="material-icons">refresh</i> 새로고침</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 전체 합산 요약 -->
|
||||||
|
<div class="bucket-summary" id="bucketSummary" style="display:none;">
|
||||||
|
<div class="bucket-summary-title"><i class="material-icons">functions</i> 전체 인스턴스 합산</div>
|
||||||
|
<div class="bucket-summary-grid">
|
||||||
|
<div class="bucket-summary-item">
|
||||||
|
<span class="bucket-summary-label">활성 인스턴스</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryInstanceCount">0<span class="unit">개</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryPerSecondWrap">
|
||||||
|
<span class="bucket-summary-label">초당 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryPerSecond">0<span class="unit">req/sec</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryPerSecondCalc"></span>
|
||||||
|
</div>
|
||||||
|
<div class="bucket-summary-item" id="summaryThresholdWrap">
|
||||||
|
<span class="bucket-summary-label">추가 임계치 (전체)</span>
|
||||||
|
<span class="bucket-summary-value" id="summaryThreshold">0<span class="unit">req</span></span>
|
||||||
|
<span class="bucket-summary-sub" id="summaryThresholdCalc"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bucket-server-list" id="bucketServerList">
|
||||||
|
<div class="bucket-empty-msg">
|
||||||
|
<i class="material-icons">hourglass_empty</i>
|
||||||
|
버킷 현황을 조회하려면 새로고침 버튼을 클릭하세요
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /tabStatusContent -->
|
||||||
|
|
||||||
|
</div><!-- end content_middle -->
|
||||||
|
</div><!-- end right_box -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
+8
@@ -85,4 +85,12 @@ public class InflowAdapterControlManController extends OnlBaseAnnotationControll
|
|||||||
return new ModelAndView("jsonView", resultMap);
|
return new ModelAndView("jsonView", resultMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/onl/admin/inflow/inflowAdapterControlMan.json", params = "cmd=LIST_BUCKET_STATUS")
|
||||||
|
public ModelAndView getBucketStatus(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
List<Map<String, Object>> statusList = service.getAdapterBucketStatus();
|
||||||
|
Map<String, Object> resultMap = new HashMap<>();
|
||||||
|
resultMap.put("servers", statusList);
|
||||||
|
return new ModelAndView("jsonView", resultMap);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+8
@@ -83,4 +83,12 @@ public class InflowClientControlManController extends OnlBaseAnnotationControlle
|
|||||||
return new ModelAndView("jsonView", resultMap);
|
return new ModelAndView("jsonView", resultMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/onl/admin/inflow/inflowClientControlMan.json", params = "cmd=LIST_BUCKET_STATUS")
|
||||||
|
public ModelAndView getBucketStatus(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
List<Map<String, Object>> statusList = service.getClientBucketStatus();
|
||||||
|
Map<String, Object> resultMap = new HashMap<>();
|
||||||
|
resultMap.put("servers", statusList);
|
||||||
|
return new ModelAndView("jsonView", resultMap);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+75
@@ -1,12 +1,21 @@
|
|||||||
package com.eactive.eai.rms.onl.manage.inflow.inflow;
|
package com.eactive.eai.rms.onl.manage.inflow.inflow;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
import com.eactive.eai.data.entity.onl.inflow.InflowControl;
|
import com.eactive.eai.data.entity.onl.inflow.InflowControl;
|
||||||
import com.eactive.eai.data.entity.onl.inflow.InflowControlId;
|
import com.eactive.eai.data.entity.onl.inflow.InflowControlId;
|
||||||
@@ -14,10 +23,13 @@ import com.eactive.eai.rms.common.base.BaseService;
|
|||||||
import com.eactive.eai.rms.common.combo.ComboService;
|
import com.eactive.eai.rms.common.combo.ComboService;
|
||||||
import com.eactive.eai.rms.data.entity.onl.inflow.InflowControlService;
|
import com.eactive.eai.rms.data.entity.onl.inflow.InflowControlService;
|
||||||
import com.eactive.eai.rms.data.entity.onl.inflow.InflowControlServiceDto;
|
import com.eactive.eai.rms.data.entity.onl.inflow.InflowControlServiceDto;
|
||||||
|
import com.eactive.eai.rms.onl.common.service.EaiServerInfoService;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class InflowControlManService extends BaseService {
|
public class InflowControlManService extends BaseService {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(InflowControlManService.class);
|
||||||
|
|
||||||
private static final String ADAPTER_TYPE_INFLOW = "01";
|
private static final String ADAPTER_TYPE_INFLOW = "01";
|
||||||
private static final String INTERFACE_TYPE_INFLOW = "02";
|
private static final String INTERFACE_TYPE_INFLOW = "02";
|
||||||
private static final String CLIENT_TYPE_INFLOW = "03";
|
private static final String CLIENT_TYPE_INFLOW = "03";
|
||||||
@@ -32,6 +44,11 @@ public class InflowControlManService extends BaseService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private InflowControlManMapper mapper;
|
private InflowControlManMapper mapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private EaiServerInfoService eaiServerInfoService;
|
||||||
|
|
||||||
|
private RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
// 어댑터 유량제어
|
// 어댑터 유량제어
|
||||||
public Page<InflowControlManUI> selectAdapterList(Pageable pageable, String searchName) {
|
public Page<InflowControlManUI> selectAdapterList(Pageable pageable, String searchName) {
|
||||||
Page<InflowControlServiceDto> dtoList = service.findAllForAdapter(pageable, searchName);
|
Page<InflowControlServiceDto> dtoList = service.findAllForAdapter(pageable, searchName);
|
||||||
@@ -123,6 +140,64 @@ public class InflowControlManService extends BaseService {
|
|||||||
InflowControlId id = toId(CLIENT_TYPE_INFLOW, name);
|
InflowControlId id = toId(CLIENT_TYPE_INFLOW, name);
|
||||||
service.findById(id).ifPresent(service::delete);
|
service.findById(id).ifPresent(service::delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<Map<String, Object>> getClientBucketStatus() {
|
||||||
|
return getBucketStatus("client", "클라이언트가 로드되지 않음", "클라이언트 버킷 상태 조회 실패");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<Map<String, Object>> getAdapterBucketStatus() {
|
||||||
|
return getBucketStatus("adapter", "어댑터가 로드되지 않음", "어댑터 버킷 상태 조회 실패");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public List<Map<String, Object>> getInterfaceBucketStatus() {
|
||||||
|
return getBucketStatus("interface", "인터페이스가 로드되지 않음", "인터페이스 버킷 상태 조회 실패");
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private List<Map<String, Object>> getBucketStatus(String type, String noDataMessage, String logPrefix) {
|
||||||
|
List<Map<String, Object>> result = new ArrayList<>();
|
||||||
|
List<Map<String, String>> serverList = eaiServerInfoService.getEaiServerIpList();
|
||||||
|
|
||||||
|
for (Map<String, String> server : serverList) {
|
||||||
|
String serverName = server.get("EAISVRINSTNM");
|
||||||
|
String serverIp = server.get("EAISVRIP");
|
||||||
|
String serverPort = server.get("WEBSERVERPORT");
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(serverPort)) {
|
||||||
|
serverPort = server.get("EAISVRLSNPORT");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> serverStatus = new HashMap<>();
|
||||||
|
serverStatus.put("serverName", serverName);
|
||||||
|
serverStatus.put("serverIp", serverIp);
|
||||||
|
serverStatus.put("serverPort", serverPort);
|
||||||
|
|
||||||
|
try {
|
||||||
|
String url = String.format("http://%s:%s/manage/inflow/%s/bucket-status",
|
||||||
|
serverIp, serverPort, type);
|
||||||
|
ResponseEntity<Map> response = restTemplate.getForEntity(url, Map.class);
|
||||||
|
|
||||||
|
if (response.getBody() != null && Boolean.TRUE.equals(response.getBody().get("success"))) {
|
||||||
|
serverStatus.put("status", "online");
|
||||||
|
serverStatus.put("data", response.getBody().get("data"));
|
||||||
|
} else {
|
||||||
|
serverStatus.put("status", "no_data");
|
||||||
|
serverStatus.put("message", noDataMessage);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.warn("{} - server: {}, error: {}", logPrefix, serverName, e.getMessage());
|
||||||
|
serverStatus.put("status", "offline");
|
||||||
|
serverStatus.put("message", "서버 연결 실패");
|
||||||
|
}
|
||||||
|
|
||||||
|
result.add(serverStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private InflowControlId toId(InflowControlManUI ui) {
|
private InflowControlId toId(InflowControlManUI ui) {
|
||||||
|
|||||||
+8
@@ -83,4 +83,12 @@ public class InflowInterfaceControlManController extends OnlBaseAnnotationContro
|
|||||||
return new ModelAndView("jsonView", resultMap);
|
return new ModelAndView("jsonView", resultMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/onl/admin/inflow/inflowInterfaceControlMan.json", params = "cmd=LIST_BUCKET_STATUS")
|
||||||
|
public ModelAndView getBucketStatus(HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
List<Map<String, Object>> statusList = service.getInterfaceBucketStatus();
|
||||||
|
Map<String, Object> resultMap = new HashMap<>();
|
||||||
|
resultMap.put("servers", statusList);
|
||||||
|
return new ModelAndView("jsonView", resultMap);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user