Compare commits

...
18 Commits
Author SHA1 Message Date
smishchuk 5401b7e96d 030 s3vol critical fix 2026-07-30 16:02:05 +03:00
smishchuk 0985d285b9 029 additional fields at root 2026-07-29 13:11:52 +03:00
smishchuk bf964bde34 027 query optimized 2026-07-28 13:08:12 +03:00
smishchuk e8df23c2e2 026 ceiling 2026-07-28 12:38:02 +03:00
smishchuk 849a4c2796 024 improvements 2026-07-26 23:04:05 +03:00
smishchuk f7421c1155 023 sslCertificateInstall 2026-07-20 20:31:27 +03:00
smishchuk 9e0fdd7b7b 022 connection diagnostics 2026-07-20 18:26:30 +03:00
smishchuk 3bc7cea1d1 021 dimensions dimension1 2026-07-20 11:06:58 +03:00
smishchuk 90aa268918 20 prices from contract 2026-05-13 10:28:36 +03:00
smishchuk caaab5e0f7 019 day loop 2026-05-11 19:50:50 +03:00
smishchuk 3e89a13883 016 refresh 2025-10-22 17:19:16 +03:00
smishchuk 3e3fa0f7ca 015 redirect 2025-10-21 19:09:30 +03:00
smishchuk ab088906b8 014 Bearer 2025-10-20 22:29:38 +03:00
smishchuk a8bfd3de9e 013 s3 cold prices added 2025-10-20 19:52:59 +03:00
smishchuk 46169b449e 012 2025-10-14 15:19:23 +03:00
smishchuk 55fa955326 012 json time format bug fix 2025-10-13 08:50:34 +03:00
smishchuk 2fdcd9598a 011 json time format 2025-10-13 08:33:10 +03:00
smishchuk bba1c0fe0c 010 json report 2025-10-10 19:51:26 +03:00
34 changed files with 6855 additions and 367 deletions
+11
View File
@@ -0,0 +1,11 @@
<hr/>
test
<hr/>
<cfflush/>
<!--- <cftry>
<cfdump var=#session#/>
<cfcatch type="any"><cfdump var=#cfcatch#/></cfcatch>
</cftry> --->
<cfmodule template="mod/crm_contract.cfm" wz="WZ01348"/>
<cfdump var=#qComponent#/>
+267 -89
View File
@@ -8,6 +8,7 @@
<!--- Set up the application. ---> <!--- Set up the application. --->
<cfset this.Name = "PAYG-REPORT" /> <cfset this.Name = "PAYG-REPORT" />
<cfset this.applicationTimeout = createTimeSpan( 0, 3, 0, 0 ) /> <cfset this.applicationTimeout = createTimeSpan( 0, 3, 0, 0 ) />
<cfset this.requestTimeout = createTimeSpan( 0, 0, 2, 0 )/>
<cfset this.sessionmanagement="Yes"/> <cfset this.sessionmanagement="Yes"/>
<cfset this.clientmanagement="No"/> <cfset this.clientmanagement="No"/>
<cfset this.sessiontimeout=CreateTimeSpan(0, 1, 0, 0)/> <cfset this.sessiontimeout=CreateTimeSpan(0, 1, 0, 0)/>
@@ -15,29 +16,32 @@
<cfset this.mappings = structNew() /> <cfset this.mappings = structNew() />
<cfset this.mappings["/mod"] = getDirectoryFromPath(getCurrentTemplatePath()) & "mod/" /> <cfset this.mappings["/mod"] = getDirectoryFromPath(getCurrentTemplatePath()) & "mod/" />
<cfset this.customTagPaths = expandPath(getDirectoryFromPath(getCurrentTemplatePath()) & "mod")/> <cfset this.customTagPaths = expandPath(getDirectoryFromPath(getCurrentTemplatePath()) & "mod")/>
<!--- <cfset this.nullSupport = true/> --->
<!--- SSO ---> <!--- SSO --->
<cfset this.client_id = "payg-report.deck.nubes.ru"/> <cfset this.client_id = "payg-report.deck.nubes.ru"/>
<cfset this.auth_endpoint = "https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/auth" /> <cfset this.auth_endpoint = "https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/auth" />
<cfset this.logout_endpoint = "https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/logout" />
<cfset this.access_token_endpoint = "https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/token" /> <cfset this.access_token_endpoint = "https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/token" />
<cfset this.idpCertUrl = "https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/certs" /> <cfset this.idpCertUrl = "https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/certs" />
<cfset this.client_secret = createObject("java", "java.lang.System").getEnv("IDP_CLIENT_SECRET")/> <cfset this.client_secret = createObject("java", "java.lang.System").getEnv("IDP_CLIENT_SECRET")/>
<!--- <cfset this.iam_url = "https://auth-api.ngcloud.ru/api/v1/auth/user"/> --->
<cfset this.iamServiceUrl = "https://auth-api-dev.ngcloud.ru/api/v1/auth/user "/>
<cfset this.redirect_uri = "https://#CGI.SERVER_NAME##CGI.SCRIPT_NAME#"/><!--- куда возвращаться от IDP --->
<cfset this.crm_cache_host = "elma-cache.adl.nubes.ru"/><!--- *** почему-то приходится устанавливать серт sslCertificateInstall, иначе не коннектится --->
<cfset request.crm_cache_base = "https://#this.crm_cache_host#/contract_ls.cfm"/>
<cfset this.datasource = "rpt"/> <cfset this.datasource = "rpt"/><!--- к буферной базе --->
<cfset this.defaultdatasource = this.datasource/> <cfset this.defaultdatasource = this.datasource/>
<cfset request.DS = "#this.datasource#"> <!--- request scope is available in pseudoconstructor
<cfset request.language=""> see also https://www.bennadel.com/blog/1437-coldfusion-scope-existence-during-various-request-types-and-events.htm --->
<cfset request.DS = "#this.datasource#"/>
<cfset request.language=""/>
<cfset getDS(this.datasource)/> <cfset getDS(this.datasource)/>
<!--- кажется, нужно инициализировать датасорцы в псевдоконструкторе - onRequest не получается ---> <!--- кажется, нужно инициализировать датасорцы в псевдоконструкторе - onRequest не получается --->
<!--- *** проверить, что есть - если есть, не создавать ---> <!--- *** проверить, что есть - если есть, не создавать --->
<!--- это работает с административно заданным датасорцем, хотя, казалось бы, не должно
(без окружения функция вернет пустую структуру). Видимо, административный датасорц обнаруживается раньше.
Проверять на пустоту не хочется, чтобы не плодить в псевдоконструкторе переменные --->
<!--- <cfdump var=#variables#/> --->
<cffunction <cffunction
name="OnApplicationStart" name="OnApplicationStart"
access="public" access="public"
@@ -45,10 +49,23 @@
output="false" output="false"
hint="Fires when the application is first created."> hint="Fires when the application is first created.">
<!--- *** Lucee specific function --->
<cfset sslCertificateInstall(host="elma-cache.adl.nubes.ru", port=443)/><!--- ************************ duplicate address --->
<!--- *** мы не проверяем срок жизни сертификата и его отзыв---> <!--- *** мы не проверяем срок жизни сертификата и его отзыв--->
<!--- Obtain idp certificate OnApplicationStart --->
<cfset application.idpCertificate = getIdpCertificate(this.idpCertUrl)/> <cfset application.idpCertificate = getIdpCertificate(this.idpCertUrl)/>
<!--- <cfset this.iamServiceUrl = locateIamService()/> --->
<!--- <cfreturn super.onApplicationStart() /> --->
<cfreturn true />
</cffunction>
<cffunction name="onRequestStart" returnType="boolean">
<cfargument name="targetPage" type="string" required="true" />
<cfif structKeyExists(url, "application_reset")>
<cfset ApplicationStop() />
<cflocation url="index.cfm" addtoken="false" />
</cfif>
<cfreturn true /> <cfreturn true />
</cffunction> </cffunction>
@@ -77,7 +94,8 @@
<!--- global settings ---> <!--- global settings --->
<cfset request.RECORDS_PER_PAGE=500/> <cfset request.RECORDS_PER_PAGE=500/>
<cfset request.APP_VERSION="0.00.010"/> <cfset request.APP_VERSION="0.00.030"/>
<cfheader name="X-Application-Version" value=#request.APP_VERSION#/>
<cfset request.STAND=getStand()/> <cfset request.STAND=getStand()/>
<!--- application constants ---> <!--- application constants --->
@@ -141,6 +159,7 @@
<cfset request.APP_NAME=this.Name/> <cfset request.APP_NAME=this.Name/>
<cfset idpCertificateExists = structKeyExists(application,"idpCertificate")/> <cfset idpCertificateExists = structKeyExists(application,"idpCertificate")/>
</cflock> </cflock>
<cfif NOT idpCertificateExists> <cfif NOT idpCertificateExists>
<!--- Obtain IDP Certificate (onRequest) ---> <!--- Obtain IDP Certificate (onRequest) --->
<cfset var idpCertificate = getIdpCertificate(this.idpCertUrl) /> <cfset var idpCertificate = getIdpCertificate(this.idpCertUrl) />
@@ -151,42 +170,137 @@
<!--- <cfoutput>onRequest:check for cert finished #(getTickCount()-request.startTickCount)#</cfoutput> ---> <!--- <cfoutput>onRequest:check for cert finished #(getTickCount()-request.startTickCount)#</cfoutput> --->
<cfset this.redirect_uri = "https://#CGI.SERVER_NAME##CGI.SCRIPT_NAME#" />
<cfinclude template="inc/functions.cfm"/>
<cfif structKeyExists(url,"logout")>
<cflock scope="session" type="exclusive" timeout="3">
<cfset structDelete(session, "auth")/>
</cflock>
</cfif>
<cfcookie name="CFID" value="#session.CFID#"/> <cfcookie name="CFID" value="#session.CFID#"/>
<cfcookie name="CFTOKEN" value="#session.CFTOKEN#"/> <cfcookie name="CFTOKEN" value="#session.CFTOKEN#"/>
<!--- SSO ---> <cfinclude template="inc/functions.cfm"/>
<cflock scope="session" type="exclusive" timeout="3">
<!---
<cfdump var=#session#/>
<cfoutput>#(getTickCount()-request.startTickCount)#</cfoutput>--->
<!---
Это первая проба, с сессией
Когда протухнет сессия, мы можем пойти к IDP и повторно аутентифицироваться. Поскольку у нас сохраняются куки IDP, его сессия, возможно, будет жива. Если мы обновляли сессию IDP в другом приложении SSO, по всей видимости, она будет продолжаться. Если мы видим, что сессия IDP состарилась и хочет протухнуть, мы можем ее освежить и получить новый access token (который мы пока не используем - мы пользуемся ответом IDP) Наверное, правильно обходиться для этих целей вообще без сессии *** --->
<!---
проверить sso
? сделать рефреш
? сделать логаут
? сделать отображение ошибки IDP
+ сделать получение сертификата
---> <!--- SSO --->
<cfif structKeyExists(session, "auth")> <!--- Если без сессии. Проверяем наличие токена в заголовке (и в куках?)
<cfset request.auth = structCopy(session.auth)/> Если его нет, идем к IDP за токеном. Получив токен, складываем его в куки
<!--- <cfset request.auth.wz = "WZ01553"/> ---> Если токен есть, распаковываем его. Проверяем протухание. Если прошла установленная часть времени жизни, идем за рефрешем (внимание: это сработает только в браузере,
<!--- тут нужно сделать проверку и рефреш токена ---> если клиент не умеет редиректы - то токен должен быть достаточно свежий)
<!--- сессия есть ---> (для начала делаем без рефреша, только проверяем протухание)
<cfelse> А рефреш можно сделать на серверной стороне?
<!--- сессии нет ---> Кладем данные из токена в реквест и наслаждаемся --->
<cfset session.auth_state=createGUID().toString()/> <!--- Вариант: если есть Authorization:bearer, идем с ним к IAM --->
<cfscript> <cfscript>
jwt = new lib.jwt(this.client_secret); var requestData = GetHttpRequestData();
idp = new lib.oauth2(this.client_id, this.client_secret, this.auth_endpoint, this.access_token_endpoint, this.redirect_uri); var headers = requestData.headers;
//var authorizationHeader = server.system.environment?.AUTHORIZATION;
if (structKeyExists(headers,"Authorization")) {
request.auth_header = headers.Authorization;
} else {
//request.auth_header = createObject("java", "java.lang.System").getEnv("AUTHORIZATION");
request.auth_header = server.system.environment?.AUTHORIZATION; //так короче
if (isNull(request.auth_header)){ /*for local debug we can inject long-living token into environment*/
structDelete(request,"auth_header");
} else {
request.auth_header = "Bearer #request.auth_header#";
}
}
// if (structKeyExists(headers,"authorization")) { // аутентификация для клиента ЛК
if (structKeyExists(request,"auth_header")) { // аутентификация для клиента ЛК
var authorizationHeader = request.auth_header;
if (authorizationHeader.startsWith("Bearer ")) {
var bearerToken = Mid(authorizationHeader, 8);
//writeDump(bearerToken);
//var token=toString(binaryDecode(bearerToken, "base64"));
//writeDump(token);
//jwt = new lib.jwt(this.client_secret);
//lock scope="application" type="readonly" timeout="1" {
// var token_data = (jwt.decode(token, application.idpCertificate,"RS256"));
//}
var result="";
//try {
var iamService = new http(method = "GET", charset = "utf-8", url = #this.iamServiceUrl#, timeout="5");
iamService.addParam(type = "HEADER", name = "Accept", value = "application/json");
iamService.addParam(type = "HEADER", name = "Authorization", value = "#authorizationHeader#"); //passthrough
//writedump(this.iamServiceUrl);abort;
var resp = iamService.send();
//if (resp.status_code NEQ 200) throw("IDP response not OK");
//writedump(resp);abort;
var prefix = resp.getPrefix();
if (prefix.status_code NEQ 200) {
//writedump(this.iamServiceUrl);
//writedump(resp);
var iamStatusCode = (isValid("integer", prefix.status_code)) ? val(prefix.status_code) : 500;
throw(message="IAM error", detail="URL: #this.iamServiceUrl# Status Code: #prefix.status_code#");
//abort;
//throw("IDP response not OK");
}
result = prefix.filecontent;
var idpUserData=deserializeJson(result);
request.auth.wz=idpUserData.userInfo.ClientId;
//writeDump(idpUserData);
request.auth.login = idpUserData.userInfo.login;
//request.usrUid=idpUserData.userInfo.contactId;//userId; //GUID!
//writedump(idpUserData);abort;
//} catch (e) {
/*if (fileExists("#GetDirectoryFromPath(GetCurrentTemplatePath())#/etc/local-debug")) { //true for local debug without IDP, etc/* does not go to repository
var result='{"accounts":[{"login":"","type":"telegram"}],"avatar":["d2d2b7ac-50af-432b-b7c4-f7d6561e288a"],"company":"ООО «НУБЕС»","companyId":"8ec70ac0-546d-42a7-8cff-339c8fb51a23","contactId":"983967a3-58c2-4cdd-84a5-8b427ccfac82","email":"smishchuk@nubes.ru","externalUser":false,"fio":{"fullName":"Мищук Сергей ","name":"Сергей","secondName":"","surname":"Мищук"},"groupIds":["94bf5be7-52f4-5c13-87c3-513786934685","ab12b6b8-0265-4683-a5c2-5e1a74a55216","aff008c3-7443-50f6-86e1-ecff3cd3b04d","d6000da0-c9aa-55eb-9882-f118b432730b","d89a33dc-3177-5854-9580-f7f860a5ab7c","ddfe2555-9ef4-42cb-9a2b-7f24e61e7747","df12926f-ecb8-5479-857f-6d291464baad","fda5c295-230a-5025-9797-b8b4e99e08aa","2be9b4b6-94d1-59f9-a649-cab228d82169","64fe6f5c-a91b-5fea-a7ca-d9823701ebd4"],"integration":{"serviceId":""},"login":"smishchuk@nubes.ru","mobilePhone":[],"position":"","userId":"d72530e1-66a4-412f-a046-38153c4e5405"}';}
*/
//writedump(resp);
//return representationOf( {"exception"=e} );
//rethrow(e);
//}
}
} else {
//точно мы так не словим дедлок ВПР
lock scope="session" type="readonly" timeout="3" {
if (structKeyExists(session, "auth")) {
request.auth = structCopy(session.auth);
}
}
if (structKeyExists(request, "auth") AND Now() < request.auth.exp) {
if (dateDiff("s", request.auth.iat, Now()) > request.auth.refresh_expires_in/1.5 AND CGI.REQUEST_METHOD EQ "GET") {
// доля времени, после которой происходит рефреш, прибита гвоздями
// По всей видимости, рефреш будет ломать обращения POSТ
// В связи с чем предлагается дождаться GET
// Приклеивать query_string как-то не пришлось, браузер сам справляется
// refresh
var idp = new lib.oauth2(this.client_id, this.client_secret, this.auth_endpoint, this.access_token_endpoint, this.redirect_uri);
var resp = idp.refreshAccessTokenRequest(request.auth.refresh_token);
lock scope="application" type="readonly" timeout="1" {
idpCertificate = application.idpCertificate;
}
var auth = parseIdpresponse(resp, idpCertificate, this.client_secret); //все эти сложности из-за опасений насчет многопоточности и блокировок
lock scope="session" type="exclusive" timeout="1" {
session.auth = auth;
}
request.auth = auth;
writeDump(auth);
}
} else {
// сессии SSO нет
// или токен протух
request.auth_state = createGUID().toString();
lock scope="session" type="exclusive" timeout="1" {
structDelete(session, "auth"); // зачистили сессию от данных SSO
session.auth_state = request.auth_state;
}
var idp = new lib.oauth2(this.client_id, this.client_secret, this.auth_endpoint, this.access_token_endpoint, this.redirect_uri);
/* /*
часто появляется ошибка: после неудачного запроса к IDP часто появляется ошибка: после неудачного запроса к IDP
в адресной строке браузера остается все, с чем его редиректил IDP, в адресной строке браузера остается все, с чем его редиректил IDP,
@@ -195,67 +309,54 @@
Не очень понятно, как предотвратить повторный запрос (можно, конечно, использовать сессию, но странно) Не очень понятно, как предотвратить повторный запрос (можно, конечно, использовать сессию, но странно)
*/ */
//writeOutput(getTickCount()-request.startTickCount); //writeOutput(getTickCount()-request.startTickCount);
if(structKeyExists(url, "code")) { //обрабатываем редирект от IDP *** наличие поля code в URL введет нас в заблуждение, но эvar = data, label =то только без валидной сессии if(structKeyExists(url, "code")) { //обрабатываем редирект от IDP *** наличие поля code в URL введет нас в заблуждение,
//dump(var = url.code, label = "URL.code");
// Request access token from idp with the
// authorization code that we got via the URL
//aFormFields = [];
//data = idp.makeAccessTokenRequest((url.code).content, aFormFields);
try { try {
writeOutput("idp.makeAccessTokenRequest"); //writeOutput("idp.makeAccessTokenRequest");
writeOutput(getTickCount()-request.startTickCount); //writeOutput(getTickCount()-request.startTickCount);
resp = idp.makeAccessTokenRequest(url.code/*, aFormFields*/); var resp = idp.makeAccessTokenRequest(url.code);
writeOutput("idp.makeAccessTokenRequest Done");
writeOutput(getTickCount()-request.startTickCount);
//dump(resp);
data = deserializeJson(resp.content);
//writedump(data);
// Print full response from idp
//dump(var = data, label = "makeAccessTokenRequest - Response from idp");
//echo('<a href="./index.cfm?refresh=#data.refresh_token#">Refresh token #data.refresh_token#</a>');
session.auth.expires_in = data.expires_in;
session.auth.refresh_expires_in = data.refresh_expires_in;
session.auth.access_token = data.access_token;
session.auth.refresh_token = data.refresh_token;
session.auth.id_token = data.id_token;
session.auth.token_type = data.token_type;
session.idp_response_content = structCopy(data);
//flush()
lock scope="application" type="readonly" timeout="1" { lock scope="application" type="readonly" timeout="1" {
token_data = (jwt.decode(data.access_token, application.idpCertificate,"RS256")); idpCertificate = application.idpCertificate;
} }
writeOutput("jwt.decode Done");
writeOutput(getTickCount()-request.startTickCount);
writedump(token_data);
session.auth.login = token_data.preferred_username;
session.auth.wz = token_data.ClientID;
session.auth.fullname = token_data.name;
session.auth.groups = token_data.groups;
session.auth.session_state = token_data.session_state;
session.auth.sid = token_data.sid;
session.auth.auth_time = token_data.auth_time;
session.auth.exp = token_data.exp;
session.auth.iat = token_data.iat;
//session.sid = token_data.sid;
session.token_data = structCopy(token_data);
var auth = parseIdpresponse(resp, idpCertificate, this.client_secret); //все эти сложности из-за опасений насчет многопоточности и блокировок
lock scope="session" type="exclusive" timeout="1" {
session.auth = auth;
}
//request.auth = auth;
location(this.redirect_uri, false); location(this.redirect_uri, false);
} catch (e) {echo('<br>****************** #e.message# : #e.detail# **********');} } catch (e) {echo('<br>****************** #e.message# : #e.detail# **************');}
} else { } else { //отправляем браузер к IDP
strURL = idp.buildRedirectToAuthURL({"scope":'openid profile email',"state":session.auth_state,"allow_signup":false}); var strURL =idp.buildRedirectToAuthURL({"scope":'openid profile email',"state":request.auth_state,"allow_signup":false});
echo('<a href="#strURL#">self-made Auth link (idp) #strURL#</a> <br> <br>'); echo('<a href="#strURL#">self-made Auth link (idp) #strURL#</a> <br> <br>');
location(strUrl,false); location(strUrl,false);
} }
}
}
//request.logout_url = "";
if (structKeyExists(request, "auth") AND structKeyExists(request.auth, "id_token")) {
request.logout_url = "#this.logout_endpoint#?id_token_hint=#request.auth.id_token#&post_logout_redirect_uri=#this.redirect_uri#?logout";
// внимание, здесь подразумевается, что redirect_uri не содержит query_string
// после возвращения от IDP нам нужно будет зачистить сессию SSO, сделаем это по слову logout
}
//request.auth.wz='WZ01348'; //*******************
//request.auth.wz='WZ01005'; //*******************
//request.auth.wz='WZ01653'; //*******************
//request.auth.wz='WZ02319'; //*******************
//request.auth.wz='WZ01762'; //*******************
//writedump(request.auth);abort;
//********************
//костыль
if (structKeyExists(URL,"wz")) { request.auth.wz=URL.wz }
//*********************
</cfscript> </cfscript>
</cfif>
</cflock><!--- session exclusive --->
<cfif structKeyExists(request, "auth")> <cfif structKeyExists(request, "auth")>
@@ -393,7 +494,7 @@
//https://keycloak.nubes.ru/realms/cloud/.well-known/openid-configuration тут ссылка //https://keycloak.nubes.ru/realms/cloud/.well-known/openid-configuration тут ссылка
//https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/certs тут серт //https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/certs тут серт
//только надо его обрамить -----BEGIN CERTIFICATE----- //только надо его обрамить -----BEGIN CERTIFICATE-----
function getIdpCertificate(certUrl) { private function getIdpCertificate(certUrl) {
var httpService = new http(); var httpService = new http();
httpService.setMethod( "get" ); httpService.setMethod( "get" );
@@ -418,5 +519,82 @@ function getIdpCertificate(certUrl) {
} }
} }
private struct function parseIdpResponse(struct resp, string certificate, string jwt_secret) {
var auth = structNew("linked");
var data = deserializeJson(arguments.resp.content);
var jwt = new lib.jwt(arguments.jwt_secret);
auth.expires_in = data.expires_in;
auth.refresh_expires_in = data.refresh_expires_in;
auth.access_token = data.access_token;
auth.refresh_token = data.refresh_token;
auth.id_token = data.id_token;
auth.token_type = data.token_type;
//idp_response_content = structCopy(data); //for debug
var token_data = (jwt.decode(data.access_token, arguments.certificate,"RS256"));
auth.session_state = token_data.session_state;
auth.sid = token_data.sid;
auth.auth_time = token_data.auth_time;
auth.exp = token_data.exp;
auth.iat = token_data.iat;
auth.login = token_data.preferred_username;
auth.wz = token_data.ClientID;
auth.fullname = token_data.name;
auth.groups = token_data.groups;
//auth.token_data = structCopy(token_data); //for debug
return (auth);
}
</cfscript> </cfscript>
<cffunction name="rethrow" returntype="void">
<!--- https://www.raymondcamden.com/2004/03/09/3089633C-9FA0-606B-3F540AE9642A795F --->
<cftry>
<cfcatch>
<cfrethrow/>
</cfcatch>
</cftry>
<cfthrow type="Context validation error" message="RETHROW() called outside TRY-CATCH"/>
</cffunction>
<!--- как определить стенд? Из персистенса. Из окружения --->
<!--- <cffunction name="locateIamService"><!--- *** архитектурный колхоз --->
<!--- почему-то мы не видим других функций, когда нас вызывают onApplicationStart. С другой стороны, а почему вообще должны вызывать без перезагрузки приложения --->
<cfset var stand=""/>
<!--- дублируем код, потому что getStand отсюда почему-то не виден --->
<cftry>
<cfquery name=qConfig>
select value as stand from config
where name='STAND'
</cfquery>
<cfset stand = qConfig.stand/>
<cfswitch expression=#stand#>
<cfcase value=",dev">
<cfreturn "https://auth-api-dev.ngcloud.ru/api/v1" />
</cfcase>
<cfcase value="test">
<cfreturn "https://auth-api-test.ngcloud.ru/api/v1" />
</cfcase>
<cfcase value="prod">
<cfreturn "https://auth-api.ngcloud.ru/api/v1" />
</cfcase>
<cfdefaultcase><!--- proceed to the legacy locator ---></cfdefaultcase>
</cfswitch>
<cfcatch type="ANY">
<!--- do nothing, proceed to the legacy locator --->
<cfrethrow/>
<cfthrow message="IAM service unavailable" detail="there is no IAM for stand #stand# defined"/>
</cfcatch>
</cftry>
</cffunction> --->
</cfcomponent> </cfcomponent>
+6
View File
@@ -0,0 +1,6 @@
корректный период
другие WZ
? full join
детализация - для S3 это бакеты
+10
View File
@@ -0,0 +1,10 @@
pvdc_map(pvdc,code) as (VALUES
('v1cl1-vsan-pvdc',''),
('v1cl2-pvdc',''),
('v2cl2-1c-pvdc',''),
('v1cl4-vsan-pvdc',''),
('v1cl6-vsan-pvdc',''),
('v1cl7-vsan-pvdc',''),
('hystax-pvdc',''),
('','')
)
+205
View File
@@ -0,0 +1,205 @@
DROP FOREIGN TABLE IF EXISTS vmreports.compute CASCADE;
DROP FOREIGN TABLE IF EXISTS vmreports.storage CASCADE;
DROP FOREIGN TABLE IF EXISTS vmreports.host CASCADE;
DROP FOREIGN TABLE IF EXISTS vmreports.vm_gpu CASCADE;
DROP FOREIGN TABLE IF EXISTS vmreports.vm_power_state CASCADE;
--------------------------------------------------------------------------------
CREATE SCHEMA IF NOT EXISTS vmreports;
ALTER SCHEMA vmreports OWNER TO dbo;
GRANT USAGE ON SCHEMA vmreports TO nifi,appserver,reader,analytics,analyst;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT SELECT ON TABLES TO appserver,reader;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT SELECT, REFERENCES ON TABLES TO appserver,analytics,analyst;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT ALL ON TABLES TO nifi;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT USAGE ON SEQUENCES TO nifi, appserver;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- TOC entry 721 (class 1259 OID 9816992)
-- Name: compute; Type: TABLE; Schema: vmreports; Owner: smishchuk
--
CREATE TABLE vmreports.compute (
"Name" character varying(255),
"UUID_VM" character varying(255),
"VDC" character varying(255),
"Organization_UUID" uuid,
"Organization" character varying(255),
"WZ" character varying(255),
"Cluster" character varying(255),
"Host" character varying(255),
"Host_Model" character varying(255),
"CPU_Model" character varying(255),
"Host_CPU_Cores" double precision,
"Host_CPU_Sockets" double precision,
"Host_CPU_Speed" double precision,
"Host_RAM" double precision,
"Host_CPU_Used" double precision,
"Host_CPU_Used_Avg" double precision,
"VDC_CPU_Reserv" double precision,
"VDC_CPU_Speed" double precision,
"VM_vCPU_count" double precision,
"VM_CPU_Used" double precision,
"VM_CPU_Used_AVG" double precision,
"VM_CPU_Reserv_GHz" double precision,
"VM_CPU_Reserv_perc" double precision,
"VM_RAM_Allocated" double precision,
"VM_RAM_Used" double precision,
"VM_is_ON" double precision,
"Object_UUID" uuid NOT NULL,
ts timestamp with time zone NOT NULL
);
ALTER TABLE vmreports.compute OWNER TO dbo;
--
-- TOC entry 722 (class 1259 OID 9817004)
-- Name: storage; Type: TABLE; Schema: vmreports; Owner: smishchuk
--
CREATE TABLE vmreports.storage (
"Name" character varying(255),
"VM_UUID" character varying(255),
"VDC" character varying(255),
"Organization_UUID" uuid,
"Organization" character varying(255),
"WZ" character varying(255),
"Cluster" character varying(255),
"Disk" character varying(255),
"Storage_Policy" character varying(255),
"Disk_Allocated" double precision,
"Disk_Used" double precision,
"VM_is_ON" double precision,
"Object_UUID" uuid NOT NULL,
ts timestamp with time zone NOT NULL
);
ALTER TABLE vmreports.storage OWNER TO dbo;
--
-- TOC entry 720 (class 1259 OID 9816984)
-- Name: host; Type: TABLE; Schema: vmreports; Owner: smishchuk
--
CREATE TABLE vmreports.host (
"Name" character varying(255),
"Model" character varying(255),
"Cluster" character varying(255),
"Maintenance" character varying(255),
"CPU_Cores" double precision,
"CPU_Sockets" double precision,
"CPU_Speed" double precision,
"RAM_Total" double precision,
"RAM_Used" double precision,
"CPU_Used" double precision,
"CPU_Used_AVG" double precision,
"CPU_Contention_95" double precision,
"Object_UUID" uuid NOT NULL,
ts timestamp with time zone NOT NULL,
"Cluster_v2" character varying(255),
"HT_Active" boolean,
"CPUtoVM" double precision,
"RAMtoVM" double precision
);
ALTER TABLE vmreports.host OWNER TO dbo;
--
-- TOC entry 733 (class 1259 OID 11093092)
-- Name: vm_gpu; Type: TABLE; Schema: vmreports; Owner: smishchuk
--
CREATE TABLE vmreports.vm_gpu (
"Name" character varying(255),
"ClientID" character varying(255),
"VCD" character varying(255),
"Org" character varying(255),
"OrgVDC" character varying(255),
"vCenter" character varying(255),
"GPU" jsonb,
"OrgStatus" character varying(255),
"Object_UUID" uuid NOT NULL,
ts timestamp with time zone NOT NULL
);
ALTER TABLE vmreports.vm_gpu OWNER TO dbo;
--
-- TOC entry 732 (class 1259 OID 11093087)
-- Name: vm_power_state; Type: TABLE; Schema: vmreports; Owner: smishchuk
--
CREATE TABLE vmreports.vm_power_state (
"Object_UUID" uuid NOT NULL,
ts timestamp with time zone NOT NULL,
value double precision
);
ALTER TABLE vmreports.vm_power_state OWNER TO dbo;
--
-- TOC entry 4761 (class 2606 OID 9816998)
-- Name: compute pk_compute; Type: CONSTRAINT; Schema: vmreports; Owner: smishchuk
--
ALTER TABLE ONLY vmreports.compute
ADD CONSTRAINT pk_compute PRIMARY KEY (ts, "Object_UUID");
--
-- TOC entry 4759 (class 2606 OID 9816990)
-- Name: host pk_host; Type: CONSTRAINT; Schema: vmreports; Owner: smishchuk
--
ALTER TABLE ONLY vmreports.host
ADD CONSTRAINT pk_host PRIMARY KEY (ts, "Object_UUID");
--
-- TOC entry 4763 (class 2606 OID 9817010)
-- Name: storage pk_storage; Type: CONSTRAINT; Schema: vmreports; Owner: smishchuk
--
ALTER TABLE ONLY vmreports.storage
ADD CONSTRAINT pk_storage PRIMARY KEY (ts, "Object_UUID");
--
-- TOC entry 4767 (class 2606 OID 11093098)
-- Name: vm_gpu pk_vm_gpu; Type: CONSTRAINT; Schema: vmreports; Owner: smishchuk
--
ALTER TABLE ONLY vmreports.vm_gpu
ADD CONSTRAINT pk_vm_gpu PRIMARY KEY (ts, "Object_UUID");
--
-- TOC entry 4765 (class 2606 OID 11093091)
-- Name: vm_power_state pk_vm_power_state; Type: CONSTRAINT; Schema: vmreports; Owner: smishchuk
--
ALTER TABLE ONLY vmreports.vm_power_state
ADD CONSTRAINT pk_vm_power_state PRIMARY KEY ("Object_UUID", ts);
--
-- TOC entry 5168 (class 0 OID 0)
-- Dependencies: 10
-- Name: SCHEMA vmreports; Type: ACL; Schema: -; Owner: dbo
--
GRANT ALL ON SCHEMA vmreports TO nifi;
GRANT ALL ON ALL TABLES IN SCHEMA vmreports TO nifi;
GRANT SELECT, REFERENCES ON ALL TABLES IN SCHEMA vmreports TO analytics, analyst;
+595
View File
@@ -0,0 +1,595 @@
база rpt сделана из базы billing (название изменено, чтобы избежать путаницы)
-- удалены последовательности, переназначены владельцы и права
-- удалены внешние ключи
-- создать роль dbo (владелец), без connect
-- создать пользователя nifi, с правом логина
-- создать пользователя appserver, с правом логина
-- дать права пользователю nifi на схемы и таблицы
-- дать права пользователю appserver на схемы и таблицы на чтение
SET ROLE postgres;
CREATE SCHEMA IF NOT EXISTS s3billing;
CREATE SCHEMA IF NOT EXISTS ngcloud_ru;
ALTER SCHEMA s3billing OWNER TO dbo;
ALTER SCHEMA ngcloud_ru OWNER TO dbo;
ALTER DATABASE rpt SET search_path TO s3billing, ngcloud_ru;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo, postgres GRANT ALL ON TABLES TO nifi;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo, postgres GRANT SELECT ON TABLES TO appserver;
SET ROLE dbo;
GRANT USAGE ON SCHEMA s3billing TO appserver,nifi;
GRANT USAGE ON SCHEMA ngcloud_ru TO appserver,nifi;
GRANT ALL ON ALL TABLES IN SCHEMA s3billing TO nifi;
GRANT SELECT ON ALL TABLES IN SCHEMA s3billing TO appserver;
GRANT ALL ON ALL TABLES IN SCHEMA ngcloud_ru TO nifi;
GRANT SELECT ON ALL TABLES IN SCHEMA ngcloud_ru TO appserver;
--GRANT ALL ON ALL SEQUENCES IN SCHEMA s3billing TO appserver,nifi;
--GRANT ALL ON ALL SEQUENCES IN SCHEMA ngcloud_ru TO appserver,nifi;
--ALTER DEFAULT PRIVILEGES FOR ROLE dbo GRANT ALL ON SEQUENCES TO appserver,nifi;
--
-- PostgreSQL database dump
--
-- Dumped from database version 16.0
-- Dumped by pg_dump version 16.4
-- Started on 2025-09-15 09:50:58
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- TOC entry 230 (class 1259 OID 2160668)
-- Name: bucket_info; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS s3billing.bucket_info;
CREATE TABLE s3billing.bucket_info (
id integer NOT NULL,
bucket_id character varying(255) NOT NULL,
name character varying(255) NOT NULL,
marker character varying(255) NOT NULL,
placement_id bigint DEFAULT 0 NOT NULL
);
--
-- TOC entry 226 (class 1259 OID 2160642)
-- Name: bucket_stat; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS s3billing.bucket_stat;
CREATE TABLE s3billing.bucket_stat (
bucket_id bigint,
num_shards bigint NOT NULL,
tenant character varying(255) DEFAULT NULL::character varying,
zone_group bigint NOT NULL,
placement_id bigint NOT NULL,
explicit_placement_data_pool character varying(255) DEFAULT NULL::character varying,
explicit_placement_data_extra_pool character varying(255) DEFAULT NULL::character varying,
explicit_placement_index_pool character varying(255) DEFAULT NULL::character varying,
index_type_id bigint NOT NULL,
versioned boolean DEFAULT false,
versioning_enabled boolean DEFAULT false,
object_lock_enabled boolean DEFAULT false,
mfa_enabled boolean DEFAULT false,
owner character varying(255) NOT NULL,
bucket_quota_enabled boolean DEFAULT false,
bucket_quota_check_on_raw boolean DEFAULT false,
bucket_quota_max_size bigint NOT NULL,
bucket_quota_max_size_kb bigint NOT NULL,
bucket_quota_max_objects bigint NOT NULL,
usage_rgw_main_size bigint NOT NULL,
usage_rgw_main_size_actual bigint NOT NULL,
usage_rgw_main_size_utilized bigint NOT NULL,
usage_rgw_main_size_kb bigint NOT NULL,
usage_rgw_main_size_kb_actual bigint NOT NULL,
usage_rgw_main_size_kb_utilized bigint NOT NULL,
usage_rgw_main_num_objects bigint NOT NULL,
usage_rgw_multimeta_size bigint,
usage_rgw_multimeta_size_actual bigint,
usage_rgw_multimeta_size_utilized bigint,
usage_rgw_multimeta_size_kb bigint,
usage_rgw_multimeta_size_kb_actual bigint,
usage_rgw_multimeta_size_kb_utilized bigint,
usage_rgw_multimeta_num_objects bigint,
creation_time timestamp without time zone NOT NULL,
mtime timestamp without time zone NOT NULL,
timestamp_addition timestamp without time zone DEFAULT date_trunc('hour'::text, CURRENT_TIMESTAMP)
);
--
-- TOC entry 245 (class 1259 OID 2160763)
-- Name: category; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS s3billing.category;
CREATE TABLE s3billing.category (
id integer NOT NULL,
name character varying(255) NOT NULL
);
--
-- TOC entry 225 (class 1259 OID 2160635)
-- Name: goose_db_version; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS s3billing.goose_db_version;
CREATE TABLE s3billing.goose_db_version (
id integer NOT NULL,
version_id bigint NOT NULL,
is_applied boolean NOT NULL,
tstamp timestamp without time zone DEFAULT now() NOT NULL
);
--
-- TOC entry 234 (class 1259 OID 2160684)
-- Name: index_type; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS index_type;
CREATE TABLE s3billing.index_type (
id integer NOT NULL,
name character varying(255) NOT NULL
);
--
-- TOC entry 232 (class 1259 OID 2160677)
-- Name: placement; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS s3billing.placement;
CREATE TABLE s3billing.placement (
id integer NOT NULL,
name character varying(255) NOT NULL
);
--
-- TOC entry 239 (class 1259 OID 2160705)
-- Name: usage_bucket_by_user; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS s3billing.usage_bucket_by_user;
CREATE TABLE s3billing.usage_bucket_by_user (
bucketid bigint,
"time" timestamp without time zone NOT NULL,
bytes_sent bigint NOT NULL,
bytes_received bigint NOT NULL,
ops bigint NOT NULL,
successful_ops bigint NOT NULL,
date_start timestamp without time zone NOT NULL,
date_end timestamp without time zone NOT NULL,
user_id bigint NOT NULL,
category_id bigint NOT NULL
);
--
-- TOC entry 238 (class 1259 OID 2160698)
-- Name: usage_stat_summary; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS s3billing.usage_stat_summary;
CREATE TABLE s3billing.usage_stat_summary (
id integer NOT NULL,
userid character varying(255) NOT NULL,
category character varying(255) NOT NULL,
bytes_sent bigint NOT NULL,
bytes_received bigint NOT NULL,
ops bigint NOT NULL,
successful_ops bigint NOT NULL,
date_start timestamp without time zone NOT NULL,
date_end timestamp without time zone NOT NULL
);
--
-- TOC entry 236 (class 1259 OID 2160691)
-- Name: usage_stat_total; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS s3billing.usage_stat_total;
CREATE TABLE s3billing.usage_stat_total (
id integer NOT NULL,
userid character varying(255) NOT NULL,
bytes_sent bigint NOT NULL,
bytes_received bigint NOT NULL,
ops bigint NOT NULL,
successful_ops bigint NOT NULL,
date_start timestamp without time zone NOT NULL,
date_end timestamp without time zone NOT NULL
);
--
-- TOC entry 243 (class 1259 OID 2160751)
-- Name: user_info; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS s3billing.user_info;
CREATE TABLE s3billing.user_info (
id integer NOT NULL,
name character varying(255) NOT NULL,
owner character varying(255) NOT NULL
);
--
-- TOC entry 241 (class 1259 OID 2160712)
-- Name: user_quota_bucket; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS s3billing.user_quota_bucket;
CREATE TABLE s3billing.user_quota_bucket (
id integer NOT NULL,
userid character varying(255) NOT NULL,
enabled boolean DEFAULT false,
check_on_raw boolean DEFAULT false,
max_size bigint NOT NULL,
max_size_kb bigint NOT NULL,
max_objects bigint NOT NULL
);
--
-- TOC entry 228 (class 1259 OID 2160661)
-- Name: zone_group; Type: TABLE; Schema: s3billing; Owner: s3billing
--
DROP TABLE IF EXISTS s3billing.zone_group;
CREATE TABLE s3billing.zone_group (
id integer NOT NULL,
name uuid NOT NULL
);
--
-- TOC entry 3278 (class 2606 OID 2160675)
-- Name: bucket_info bucket_info_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.bucket_info
ADD CONSTRAINT bucket_info_pkey PRIMARY KEY (id);
--
-- TOC entry 3273 (class 2606 OID 2476484)
-- Name: bucket_stat bucket_stat_unique; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
--ALTER TABLE ONLY s3billing.bucket_stat
-- ADD CONSTRAINT bucket_stat_unique UNIQUE (bucket_id, placement_id, timestamp_addition);
ALTER TABLE ONLY s3billing.bucket_stat
ADD CONSTRAINT bucket_stat_pkey PRIMARY KEY (bucket_id, placement_id, timestamp_addition);
--
-- TOC entry 3298 (class 2606 OID 2160768)
-- Name: category category_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.category
ADD CONSTRAINT category_pkey PRIMARY KEY (id);
--
-- TOC entry 3270 (class 2606 OID 2160640)
-- Name: goose_db_version goose_db_version_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.goose_db_version
ADD CONSTRAINT goose_db_version_pkey PRIMARY KEY (id);
--
-- TOC entry 3284 (class 2606 OID 2160689)
-- Name: index_type index_type_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.index_type
ADD CONSTRAINT index_type_pkey PRIMARY KEY (id);
--
-- TOC entry 3282 (class 2606 OID 2160682)
-- Name: placement placement_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.placement
ADD CONSTRAINT placement_pkey PRIMARY KEY (id);
--
-- TOC entry 3290 (class 2606 OID 2476705)
-- Name: usage_bucket_by_user usage_bucket_by_user_unique; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
--ALTER TABLE ONLY s3billing.usage_bucket_by_user
-- ADD CONSTRAINT usage_bucket_by_user_unique UNIQUE (bucketid, date_start, date_end, user_id, category_id);
ALTER TABLE ONLY s3billing.usage_bucket_by_user
ADD CONSTRAINT usage_bucket_by_user_pkey PRIMARY KEY (bucketid, date_start, date_end, user_id, category_id);
--
-- TOC entry 3288 (class 2606 OID 2160703)
-- Name: usage_stat_summary usage_stat_summary_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.usage_stat_summary
ADD CONSTRAINT usage_stat_summary_pkey PRIMARY KEY (id);
--
-- TOC entry 3286 (class 2606 OID 2160696)
-- Name: usage_stat_total usage_stat_total_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.usage_stat_total
ADD CONSTRAINT usage_stat_total_pkey PRIMARY KEY (id);
--
-- TOC entry 3295 (class 2606 OID 2160758)
-- Name: user_info user_info_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.user_info
ADD CONSTRAINT user_info_pkey PRIMARY KEY (id);
--
-- TOC entry 3292 (class 2606 OID 2160719)
-- Name: user_quota_bucket user_quota_bucket_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.user_quota_bucket
ADD CONSTRAINT user_quota_bucket_pkey PRIMARY KEY (id);
--
-- TOC entry 3275 (class 2606 OID 2160666)
-- Name: zone_group zone_group_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.zone_group
ADD CONSTRAINT zone_group_pkey PRIMARY KEY (id);
--
-- TOC entry 3276 (class 1259 OID 2474077)
-- Name: bucket_info_name_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE INDEX bucket_info_name_idx ON s3billing.bucket_info USING btree (name);
--
-- TOC entry 3279 (class 1259 OID 2474078)
-- Name: bucket_info_placement_id_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE INDEX bucket_info_placement_id_idx ON s3billing.bucket_info USING btree (placement_id);
--
-- TOC entry 3271 (class 1259 OID 2474094)
-- Name: bucket_stat_owner_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE INDEX bucket_stat_owner_idx ON s3billing.bucket_stat USING btree (owner);
--
-- TOC entry 3296 (class 1259 OID 2474445)
-- Name: category_name_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE UNIQUE INDEX category_name_idx ON s3billing.category USING btree (name);
--
-- TOC entry 3280 (class 1259 OID 2474468)
-- Name: placement_name_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE UNIQUE INDEX placement_name_idx ON s3billing.placement USING btree (name);
--
-- TOC entry 3293 (class 1259 OID 2474438)
-- Name: user_info_name_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE INDEX user_info_name_idx ON s3billing.user_info USING btree (name);
-- Completed on 2025-09-15 09:50:59
--
-- PostgreSQL database dump complete
--
-------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
DROP TABLE IF EXISTS ngcloud_ru.capacity_resource;
CREATE TABLE ngcloud_ru.capacity_resource (
vdc_id uuid NOT NULL,
"timestamp" timestamp with time zone NOT NULL,
cpu_allocated integer NOT NULL,
cpu_limit integer NOT NULL,
cpu_reserved integer NOT NULL,
cpu_used integer NOT NULL,
cpu_speed integer NOT NULL,
mem_allocated integer NOT NULL,
mem_limit integer NOT NULL,
mem_reserved integer NOT NULL,
mem_used integer NOT NULL
);
--
-- TOC entry 223 (class 1259 OID 1893993)
-- Name: capacity_storage; Type: TABLE; Schema: ngcloud_ru; Owner: vc_billing
--
DROP TABLE IF EXISTS ngcloud_ru.capacity_storage;
CREATE TABLE ngcloud_ru.capacity_storage (
vdc_id uuid NOT NULL,
storage_profile_types_id integer NOT NULL,
"timestamp" timestamp with time zone NOT NULL,
id uuid NOT NULL,
"limit" integer NOT NULL,
used integer NOT NULL
);
--
-- TOC entry 220 (class 1259 OID 1893965)
-- Name: storage_profile_types; Type: TABLE; Schema: ngcloud_ru; Owner: vc_billing
--
DROP TABLE IF EXISTS ngcloud_ru.storage_profile_types;
CREATE TABLE ngcloud_ru.storage_profile_types (
id integer NOT NULL,
name character varying(255) NOT NULL
);
--
-- TOC entry 218 (class 1259 OID 1893959)
-- Name: tenant; Type: TABLE; Schema: ngcloud_ru; Owner: vc_billing
--
DROP TABLE IF EXISTS ngcloud_ru.tenant;
CREATE TABLE ngcloud_ru.tenant (
id uuid NOT NULL,
name character varying(255) NOT NULL,
wzcode bigint
);
--
-- TOC entry 221 (class 1259 OID 1893973)
-- Name: vdc; Type: TABLE; Schema: ngcloud_ru; Owner: vc_billing
--
DROP TABLE IF EXISTS ngcloud_ru.vdc;
CREATE TABLE ngcloud_ru.vdc (
id uuid NOT NULL,
tenant_id uuid NOT NULL,
name character varying(255) NOT NULL
);
--
-- TOC entry 3255 (class 2606 OID 1893987)
-- Name: capacity_resource capacity_resource_pkey; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.capacity_resource
ADD CONSTRAINT capacity_resource_pkey PRIMARY KEY (vdc_id, "timestamp");
--
-- TOC entry 3257 (class 2606 OID 1893997)
-- Name: capacity_storage capacity_storage_pkey; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.capacity_storage
ADD CONSTRAINT capacity_storage_pkey PRIMARY KEY (vdc_id, id, "timestamp");
--
-- TOC entry 3249 (class 2606 OID 1893972)
-- Name: storage_profile_types storage_profile_types_name_key; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.storage_profile_types
ADD CONSTRAINT storage_profile_types_name_key UNIQUE (name);
--
-- TOC entry 3251 (class 2606 OID 1893970)
-- Name: storage_profile_types storage_profile_types_pkey; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.storage_profile_types
ADD CONSTRAINT storage_profile_types_pkey PRIMARY KEY (id);
--
-- TOC entry 3247 (class 2606 OID 1893963)
-- Name: tenant tenant_pkey; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.tenant
ADD CONSTRAINT tenant_pkey PRIMARY KEY (id);
--
-- TOC entry 3253 (class 2606 OID 1893977)
-- Name: vdc vdc_pkey; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.vdc
ADD CONSTRAINT vdc_pkey PRIMARY KEY (id);
--
-- TOC entry 3259 (class 2606 OID 1893988)
-- Name: capacity_resource capacity_resource_vdc_id_fkey; Type: FK CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
--ALTER TABLE ONLY ngcloud_ru.capacity_resource
-- ADD CONSTRAINT capacity_resource_vdc_id_fkey FOREIGN KEY (vdc_id) REFERENCES ngcloud_ru.vdc(id) ON DELETE CASCADE;
--
-- TOC entry 3260 (class 2606 OID 1894003)
-- Name: capacity_storage capacity_storage_storage_profile_types_id_fkey; Type: FK CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
--ALTER TABLE ONLY ngcloud_ru.capacity_storage
-- ADD CONSTRAINT capacity_storage_storage_profile_types_id_fkey FOREIGN KEY (storage_profile_types_id) REFERENCES ngcloud_ru.storage_profile_types(id);
--
-- TOC entry 3261 (class 2606 OID 1893998)
-- Name: capacity_storage capacity_storage_vdc_id_fkey; Type: FK CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
--ALTER TABLE ONLY ngcloud_ru.capacity_storage
-- ADD CONSTRAINT capacity_storage_vdc_id_fkey FOREIGN KEY (vdc_id) REFERENCES ngcloud_ru.vdc(id) ON DELETE CASCADE;
--
-- TOC entry 3258 (class 2606 OID 1893978)
-- Name: vdc vdc_tenant_id_fkey; Type: FK CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
--ALTER TABLE ONLY ngcloud_ru.vdc
-- ADD CONSTRAINT vdc_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES ngcloud_ru.tenant(id);
-- Completed on 2025-09-15 09:53:18
--
-- PostgreSQL database dump complete
--
File diff suppressed because it is too large Load Diff
+72
View File
@@ -0,0 +1,72 @@
set role postgres;
--
CREATE SCHEMA IF NOT EXISTS gpu;
--ALTER SCHEMA gpu OWNER TO dbo;
GRANT USAGE ON SCHEMA gpu TO nifi;
GRANT ALL ON ALL TABLES IN SCHEMA gpu TO nifi;
GRANT ALL ON ALL SEQUENCES IN SCHEMA gpu TO nifi;
--ALTER DEFAULT PRIVILEGES IN SCHEMA gpu GRANT ALL ON TABLES TO nifi;
--ALTER DEFAULT PRIVILEGES IN SCHEMA gpu GRANT ALL ON SEQUENCES TO nifi;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo, postgres IN SCHEMA gpu GRANT ALL ON TABLES TO nifi;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo, postgres IN SCHEMA gpu GRANT ALL ON SEQUENCES TO nifi;
--SET ROLE dbo;
DROP TABLE IF EXISTS gpu.vm;
CREATE TABLE gpu.vm (
objUUID uuid NOT NULL PRIMARY KEY
,groupUUID uuid NULL
,objId varchar(255) NULL
,ClientID varchar(255) NULL -- WZ
,VCD varchar(255) NULL
,Org varchar(255) NULL
,OrgVDC varchar(255) NULL
,vCenter varchar(255) NULL
,GPU varchar(255) NULL
,dt_load timestamptz NOT NULL
);
ALTER TABLE gpu.vm OWNER TO dbo;
/*
{
"type": "record",
"name": "gpu.vm",
"namespace": "org.apache.nifi",
"fields": [
{"name":"objUUID", "type":{"type": "string", "logicalType": "uuid"}},
{"name":"groupUUID", "type":[{"type": "string", "logicalType": "uuid"} ,"null"]},
{"name":"objId", "type":["string" ,"null"]},
{"name":"ClientID", "type":["int" ,"null"]},
{"name":"VCD", "type":["string" ,"null"]},
{"name":"Org", "type":["string" ,"null"]},
{"name":"OrgVDC", "type":["string" ,"null"]},
{"name":"vCenter", "type":["string" ,"null"]},
{"name":"GPU", "type":["string" ,"null"]},
{"name":"VCD", "type":["string" ,"null"]},
{"name":"dt_load","type":{"type": "long", "logicalType": "local-timestamp-millis"}}
]
}
*/
DROP TABLE IF EXISTS gpu.vm_power_state;
CREATE TABLE gpu.vm_power_state (
objId varchar(255) NULL
,ts timestamptz NOT NULL -- без таймштампа загрузки
,value int NULL
,CONSTRAINT PK_vm_power_state PRIMARY KEY(objId, ts)
);
ALTER TABLE gpu.vm_power_state OWNER TO dbo;
/*
{
"type": "record",
"name": "gpu.vm_power_state,
"namespace": "org.apache.nifi",
"fields": [
{"name":"objId", "type":["string" ,"null"]},
{"name":"ts","type":{"type": "long", "logicalType": "local-timestamp-millis"}},
{"name":"value", "type":["int" ,"null"]}
]
}
*/
+828
View File
@@ -0,0 +1,828 @@
--
-- PostgreSQL database dump
--
-- Dumped from database version 16.0
-- Dumped by pg_dump version 16.4
-- Started on 2025-09-15 09:50:58
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- TOC entry 230 (class 1259 OID 2160668)
-- Name: bucket_info; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.bucket_info (
id integer NOT NULL,
bucket_id character varying(255) NOT NULL,
name character varying(255) NOT NULL,
marker character varying(255) NOT NULL,
placement_id bigint DEFAULT 0 NOT NULL
);
ALTER TABLE s3billing.bucket_info OWNER TO s3billing;
--
-- TOC entry 229 (class 1259 OID 2160667)
-- Name: bucket_info_id_seq; Type: SEQUENCE; Schema: s3billing; Owner: s3billing
--
CREATE SEQUENCE s3billing.bucket_info_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE s3billing.bucket_info_id_seq OWNER TO s3billing;
--
-- TOC entry 3448 (class 0 OID 0)
-- Dependencies: 229
-- Name: bucket_info_id_seq; Type: SEQUENCE OWNED BY; Schema: s3billing; Owner: s3billing
--
ALTER SEQUENCE s3billing.bucket_info_id_seq OWNED BY s3billing.bucket_info.id;
--
-- TOC entry 226 (class 1259 OID 2160642)
-- Name: bucket_stat; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.bucket_stat (
bucket_id bigint,
num_shards bigint NOT NULL,
tenant character varying(255) DEFAULT NULL::character varying,
zone_group bigint NOT NULL,
placement_id bigint NOT NULL,
explicit_placement_data_pool character varying(255) DEFAULT NULL::character varying,
explicit_placement_data_extra_pool character varying(255) DEFAULT NULL::character varying,
explicit_placement_index_pool character varying(255) DEFAULT NULL::character varying,
index_type_id bigint NOT NULL,
versioned boolean DEFAULT false,
versioning_enabled boolean DEFAULT false,
object_lock_enabled boolean DEFAULT false,
mfa_enabled boolean DEFAULT false,
owner character varying(255) NOT NULL,
bucket_quota_enabled boolean DEFAULT false,
bucket_quota_check_on_raw boolean DEFAULT false,
bucket_quota_max_size bigint NOT NULL,
bucket_quota_max_size_kb bigint NOT NULL,
bucket_quota_max_objects bigint NOT NULL,
usage_rgw_main_size bigint NOT NULL,
usage_rgw_main_size_actual bigint NOT NULL,
usage_rgw_main_size_utilized bigint NOT NULL,
usage_rgw_main_size_kb bigint NOT NULL,
usage_rgw_main_size_kb_actual bigint NOT NULL,
usage_rgw_main_size_kb_utilized bigint NOT NULL,
usage_rgw_main_num_objects bigint NOT NULL,
usage_rgw_multimeta_size bigint,
usage_rgw_multimeta_size_actual bigint,
usage_rgw_multimeta_size_utilized bigint,
usage_rgw_multimeta_size_kb bigint,
usage_rgw_multimeta_size_kb_actual bigint,
usage_rgw_multimeta_size_kb_utilized bigint,
usage_rgw_multimeta_num_objects bigint,
creation_time timestamp without time zone NOT NULL,
mtime timestamp without time zone NOT NULL,
timestamp_addition timestamp without time zone DEFAULT date_trunc('hour'::text, CURRENT_TIMESTAMP)
);
ALTER TABLE s3billing.bucket_stat OWNER TO s3billing;
--
-- TOC entry 245 (class 1259 OID 2160763)
-- Name: category; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.category (
id integer NOT NULL,
name character varying(255) NOT NULL
);
ALTER TABLE s3billing.category OWNER TO s3billing;
--
-- TOC entry 244 (class 1259 OID 2160762)
-- Name: category_id_seq; Type: SEQUENCE; Schema: s3billing; Owner: s3billing
--
CREATE SEQUENCE s3billing.category_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE s3billing.category_id_seq OWNER TO s3billing;
--
-- TOC entry 3451 (class 0 OID 0)
-- Dependencies: 244
-- Name: category_id_seq; Type: SEQUENCE OWNED BY; Schema: s3billing; Owner: s3billing
--
ALTER SEQUENCE s3billing.category_id_seq OWNED BY s3billing.category.id;
--
-- TOC entry 225 (class 1259 OID 2160635)
-- Name: goose_db_version; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.goose_db_version (
id integer NOT NULL,
version_id bigint NOT NULL,
is_applied boolean NOT NULL,
tstamp timestamp without time zone DEFAULT now() NOT NULL
);
ALTER TABLE s3billing.goose_db_version OWNER TO s3billing;
--
-- TOC entry 224 (class 1259 OID 2160634)
-- Name: goose_db_version_id_seq; Type: SEQUENCE; Schema: s3billing; Owner: s3billing
--
ALTER TABLE s3billing.goose_db_version ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY (
SEQUENCE NAME s3billing.goose_db_version_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1
);
--
-- TOC entry 234 (class 1259 OID 2160684)
-- Name: index_type; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.index_type (
id integer NOT NULL,
name character varying(255) NOT NULL
);
ALTER TABLE s3billing.index_type OWNER TO s3billing;
--
-- TOC entry 233 (class 1259 OID 2160683)
-- Name: index_type_id_seq; Type: SEQUENCE; Schema: s3billing; Owner: s3billing
--
CREATE SEQUENCE s3billing.index_type_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE s3billing.index_type_id_seq OWNER TO s3billing;
--
-- TOC entry 3454 (class 0 OID 0)
-- Dependencies: 233
-- Name: index_type_id_seq; Type: SEQUENCE OWNED BY; Schema: s3billing; Owner: s3billing
--
ALTER SEQUENCE s3billing.index_type_id_seq OWNED BY s3billing.index_type.id;
--
-- TOC entry 232 (class 1259 OID 2160677)
-- Name: placement; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.placement (
id integer NOT NULL,
name character varying(255) NOT NULL
);
ALTER TABLE s3billing.placement OWNER TO s3billing;
--
-- TOC entry 231 (class 1259 OID 2160676)
-- Name: placement_id_seq; Type: SEQUENCE; Schema: s3billing; Owner: s3billing
--
CREATE SEQUENCE s3billing.placement_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE s3billing.placement_id_seq OWNER TO s3billing;
--
-- TOC entry 3456 (class 0 OID 0)
-- Dependencies: 231
-- Name: placement_id_seq; Type: SEQUENCE OWNED BY; Schema: s3billing; Owner: s3billing
--
ALTER SEQUENCE s3billing.placement_id_seq OWNED BY s3billing.placement.id;
--
-- TOC entry 239 (class 1259 OID 2160705)
-- Name: usage_bucket_by_user; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.usage_bucket_by_user (
bucketid bigint,
"time" timestamp without time zone NOT NULL,
bytes_sent bigint NOT NULL,
bytes_received bigint NOT NULL,
ops bigint NOT NULL,
successful_ops bigint NOT NULL,
date_start timestamp without time zone NOT NULL,
date_end timestamp without time zone NOT NULL,
user_id bigint NOT NULL,
category_id bigint NOT NULL
);
ALTER TABLE s3billing.usage_bucket_by_user OWNER TO s3billing;
--
-- TOC entry 238 (class 1259 OID 2160698)
-- Name: usage_stat_summary; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.usage_stat_summary (
id integer NOT NULL,
userid character varying(255) NOT NULL,
category character varying(255) NOT NULL,
bytes_sent bigint NOT NULL,
bytes_received bigint NOT NULL,
ops bigint NOT NULL,
successful_ops bigint NOT NULL,
date_start timestamp without time zone NOT NULL,
date_end timestamp without time zone NOT NULL
);
ALTER TABLE s3billing.usage_stat_summary OWNER TO s3billing;
--
-- TOC entry 237 (class 1259 OID 2160697)
-- Name: usage_stat_summary_id_seq; Type: SEQUENCE; Schema: s3billing; Owner: s3billing
--
CREATE SEQUENCE s3billing.usage_stat_summary_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE s3billing.usage_stat_summary_id_seq OWNER TO s3billing;
--
-- TOC entry 3459 (class 0 OID 0)
-- Dependencies: 237
-- Name: usage_stat_summary_id_seq; Type: SEQUENCE OWNED BY; Schema: s3billing; Owner: s3billing
--
ALTER SEQUENCE s3billing.usage_stat_summary_id_seq OWNED BY s3billing.usage_stat_summary.id;
--
-- TOC entry 236 (class 1259 OID 2160691)
-- Name: usage_stat_total; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.usage_stat_total (
id integer NOT NULL,
userid character varying(255) NOT NULL,
bytes_sent bigint NOT NULL,
bytes_received bigint NOT NULL,
ops bigint NOT NULL,
successful_ops bigint NOT NULL,
date_start timestamp without time zone NOT NULL,
date_end timestamp without time zone NOT NULL
);
ALTER TABLE s3billing.usage_stat_total OWNER TO s3billing;
--
-- TOC entry 235 (class 1259 OID 2160690)
-- Name: usage_stat_total_id_seq; Type: SEQUENCE; Schema: s3billing; Owner: s3billing
--
CREATE SEQUENCE s3billing.usage_stat_total_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE s3billing.usage_stat_total_id_seq OWNER TO s3billing;
--
-- TOC entry 3461 (class 0 OID 0)
-- Dependencies: 235
-- Name: usage_stat_total_id_seq; Type: SEQUENCE OWNED BY; Schema: s3billing; Owner: s3billing
--
ALTER SEQUENCE s3billing.usage_stat_total_id_seq OWNED BY s3billing.usage_stat_total.id;
--
-- TOC entry 243 (class 1259 OID 2160751)
-- Name: user_info; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.user_info (
id integer NOT NULL,
name character varying(255) NOT NULL,
owner character varying(255) NOT NULL
);
ALTER TABLE s3billing.user_info OWNER TO s3billing;
--
-- TOC entry 242 (class 1259 OID 2160750)
-- Name: user_info_id_seq; Type: SEQUENCE; Schema: s3billing; Owner: s3billing
--
CREATE SEQUENCE s3billing.user_info_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE s3billing.user_info_id_seq OWNER TO s3billing;
--
-- TOC entry 3463 (class 0 OID 0)
-- Dependencies: 242
-- Name: user_info_id_seq; Type: SEQUENCE OWNED BY; Schema: s3billing; Owner: s3billing
--
ALTER SEQUENCE s3billing.user_info_id_seq OWNED BY s3billing.user_info.id;
--
-- TOC entry 241 (class 1259 OID 2160712)
-- Name: user_quota_bucket; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.user_quota_bucket (
id integer NOT NULL,
userid character varying(255) NOT NULL,
enabled boolean DEFAULT false,
check_on_raw boolean DEFAULT false,
max_size bigint NOT NULL,
max_size_kb bigint NOT NULL,
max_objects bigint NOT NULL
);
ALTER TABLE s3billing.user_quota_bucket OWNER TO s3billing;
--
-- TOC entry 240 (class 1259 OID 2160711)
-- Name: user_quota_bucket_id_seq; Type: SEQUENCE; Schema: s3billing; Owner: s3billing
--
CREATE SEQUENCE s3billing.user_quota_bucket_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE s3billing.user_quota_bucket_id_seq OWNER TO s3billing;
--
-- TOC entry 3465 (class 0 OID 0)
-- Dependencies: 240
-- Name: user_quota_bucket_id_seq; Type: SEQUENCE OWNED BY; Schema: s3billing; Owner: s3billing
--
ALTER SEQUENCE s3billing.user_quota_bucket_id_seq OWNED BY s3billing.user_quota_bucket.id;
--
-- TOC entry 228 (class 1259 OID 2160661)
-- Name: zone_group; Type: TABLE; Schema: s3billing; Owner: s3billing
--
CREATE TABLE s3billing.zone_group (
id integer NOT NULL,
name uuid NOT NULL
);
ALTER TABLE s3billing.zone_group OWNER TO s3billing;
--
-- TOC entry 227 (class 1259 OID 2160660)
-- Name: zone_group_id_seq; Type: SEQUENCE; Schema: s3billing; Owner: s3billing
--
CREATE SEQUENCE s3billing.zone_group_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE s3billing.zone_group_id_seq OWNER TO s3billing;
--
-- TOC entry 3467 (class 0 OID 0)
-- Dependencies: 227
-- Name: zone_group_id_seq; Type: SEQUENCE OWNED BY; Schema: s3billing; Owner: s3billing
--
ALTER SEQUENCE s3billing.zone_group_id_seq OWNED BY s3billing.zone_group.id;
--
-- TOC entry 3258 (class 2604 OID 2160671)
-- Name: bucket_info id; Type: DEFAULT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.bucket_info ALTER COLUMN id SET DEFAULT nextval('s3billing.bucket_info_id_seq'::regclass);
--
-- TOC entry 3268 (class 2604 OID 2160766)
-- Name: category id; Type: DEFAULT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.category ALTER COLUMN id SET DEFAULT nextval('s3billing.category_id_seq'::regclass);
--
-- TOC entry 3261 (class 2604 OID 2160687)
-- Name: index_type id; Type: DEFAULT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.index_type ALTER COLUMN id SET DEFAULT nextval('s3billing.index_type_id_seq'::regclass);
--
-- TOC entry 3260 (class 2604 OID 2160680)
-- Name: placement id; Type: DEFAULT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.placement ALTER COLUMN id SET DEFAULT nextval('s3billing.placement_id_seq'::regclass);
--
-- TOC entry 3263 (class 2604 OID 2160701)
-- Name: usage_stat_summary id; Type: DEFAULT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.usage_stat_summary ALTER COLUMN id SET DEFAULT nextval('s3billing.usage_stat_summary_id_seq'::regclass);
--
-- TOC entry 3262 (class 2604 OID 2160694)
-- Name: usage_stat_total id; Type: DEFAULT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.usage_stat_total ALTER COLUMN id SET DEFAULT nextval('s3billing.usage_stat_total_id_seq'::regclass);
--
-- TOC entry 3267 (class 2604 OID 2160754)
-- Name: user_info id; Type: DEFAULT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.user_info ALTER COLUMN id SET DEFAULT nextval('s3billing.user_info_id_seq'::regclass);
--
-- TOC entry 3264 (class 2604 OID 2160715)
-- Name: user_quota_bucket id; Type: DEFAULT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.user_quota_bucket ALTER COLUMN id SET DEFAULT nextval('s3billing.user_quota_bucket_id_seq'::regclass);
--
-- TOC entry 3257 (class 2604 OID 2160664)
-- Name: zone_group id; Type: DEFAULT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.zone_group ALTER COLUMN id SET DEFAULT nextval('s3billing.zone_group_id_seq'::regclass);
--
-- TOC entry 3278 (class 2606 OID 2160675)
-- Name: bucket_info bucket_info_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.bucket_info
ADD CONSTRAINT bucket_info_pkey PRIMARY KEY (id);
--
-- TOC entry 3273 (class 2606 OID 2476484)
-- Name: bucket_stat bucket_stat_unique; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.bucket_stat
ADD CONSTRAINT bucket_stat_unique UNIQUE (bucket_id, placement_id, timestamp_addition);
--
-- TOC entry 3298 (class 2606 OID 2160768)
-- Name: category category_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.category
ADD CONSTRAINT category_pkey PRIMARY KEY (id);
--
-- TOC entry 3270 (class 2606 OID 2160640)
-- Name: goose_db_version goose_db_version_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.goose_db_version
ADD CONSTRAINT goose_db_version_pkey PRIMARY KEY (id);
--
-- TOC entry 3284 (class 2606 OID 2160689)
-- Name: index_type index_type_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.index_type
ADD CONSTRAINT index_type_pkey PRIMARY KEY (id);
--
-- TOC entry 3282 (class 2606 OID 2160682)
-- Name: placement placement_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.placement
ADD CONSTRAINT placement_pkey PRIMARY KEY (id);
--
-- TOC entry 3290 (class 2606 OID 2476705)
-- Name: usage_bucket_by_user usage_bucket_by_user_unique; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.usage_bucket_by_user
ADD CONSTRAINT usage_bucket_by_user_unique UNIQUE (bucketid, date_start, date_end, user_id, category_id);
--
-- TOC entry 3288 (class 2606 OID 2160703)
-- Name: usage_stat_summary usage_stat_summary_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.usage_stat_summary
ADD CONSTRAINT usage_stat_summary_pkey PRIMARY KEY (id);
--
-- TOC entry 3286 (class 2606 OID 2160696)
-- Name: usage_stat_total usage_stat_total_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.usage_stat_total
ADD CONSTRAINT usage_stat_total_pkey PRIMARY KEY (id);
--
-- TOC entry 3295 (class 2606 OID 2160758)
-- Name: user_info user_info_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.user_info
ADD CONSTRAINT user_info_pkey PRIMARY KEY (id);
--
-- TOC entry 3292 (class 2606 OID 2160719)
-- Name: user_quota_bucket user_quota_bucket_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.user_quota_bucket
ADD CONSTRAINT user_quota_bucket_pkey PRIMARY KEY (id);
--
-- TOC entry 3275 (class 2606 OID 2160666)
-- Name: zone_group zone_group_pkey; Type: CONSTRAINT; Schema: s3billing; Owner: s3billing
--
ALTER TABLE ONLY s3billing.zone_group
ADD CONSTRAINT zone_group_pkey PRIMARY KEY (id);
--
-- TOC entry 3276 (class 1259 OID 2474077)
-- Name: bucket_info_name_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE INDEX bucket_info_name_idx ON s3billing.bucket_info USING btree (name);
--
-- TOC entry 3279 (class 1259 OID 2474078)
-- Name: bucket_info_placement_id_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE INDEX bucket_info_placement_id_idx ON s3billing.bucket_info USING btree (placement_id);
--
-- TOC entry 3271 (class 1259 OID 2474094)
-- Name: bucket_stat_owner_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE INDEX bucket_stat_owner_idx ON s3billing.bucket_stat USING btree (owner);
--
-- TOC entry 3296 (class 1259 OID 2474445)
-- Name: category_name_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE UNIQUE INDEX category_name_idx ON s3billing.category USING btree (name);
--
-- TOC entry 3280 (class 1259 OID 2474468)
-- Name: placement_name_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE UNIQUE INDEX placement_name_idx ON s3billing.placement USING btree (name);
--
-- TOC entry 3293 (class 1259 OID 2474438)
-- Name: user_info_name_idx; Type: INDEX; Schema: s3billing; Owner: s3billing
--
CREATE INDEX user_info_name_idx ON s3billing.user_info USING btree (name);
--
-- TOC entry 3447 (class 0 OID 0)
-- Dependencies: 230
-- Name: TABLE bucket_info; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.bucket_info TO grafanabilling;
--
-- TOC entry 3449 (class 0 OID 0)
-- Dependencies: 226
-- Name: TABLE bucket_stat; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.bucket_stat TO grafanabilling;
--
-- TOC entry 3450 (class 0 OID 0)
-- Dependencies: 245
-- Name: TABLE category; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.category TO grafanabilling;
--
-- TOC entry 3452 (class 0 OID 0)
-- Dependencies: 225
-- Name: TABLE goose_db_version; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.goose_db_version TO grafanabilling;
--
-- TOC entry 3453 (class 0 OID 0)
-- Dependencies: 234
-- Name: TABLE index_type; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.index_type TO grafanabilling;
--
-- TOC entry 3455 (class 0 OID 0)
-- Dependencies: 232
-- Name: TABLE placement; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.placement TO grafanabilling;
--
-- TOC entry 3457 (class 0 OID 0)
-- Dependencies: 239
-- Name: TABLE usage_bucket_by_user; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.usage_bucket_by_user TO grafanabilling;
--
-- TOC entry 3458 (class 0 OID 0)
-- Dependencies: 238
-- Name: TABLE usage_stat_summary; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.usage_stat_summary TO grafanabilling;
--
-- TOC entry 3460 (class 0 OID 0)
-- Dependencies: 236
-- Name: TABLE usage_stat_total; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.usage_stat_total TO grafanabilling;
--
-- TOC entry 3462 (class 0 OID 0)
-- Dependencies: 243
-- Name: TABLE user_info; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.user_info TO grafanabilling;
--
-- TOC entry 3464 (class 0 OID 0)
-- Dependencies: 241
-- Name: TABLE user_quota_bucket; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.user_quota_bucket TO grafanabilling;
--
-- TOC entry 3466 (class 0 OID 0)
-- Dependencies: 228
-- Name: TABLE zone_group; Type: ACL; Schema: s3billing; Owner: s3billing
--
GRANT SELECT ON TABLE s3billing.zone_group TO grafanabilling;
-- Completed on 2025-09-15 09:50:59
--
-- PostgreSQL database dump complete
--
+278
View File
@@ -0,0 +1,278 @@
--
-- PostgreSQL database dump
--
-- Dumped from database version 16.0
-- Dumped by pg_dump version 16.4
-- Started on 2025-09-15 09:53:16
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- TOC entry 222 (class 1259 OID 1893983)
-- Name: capacity_resource; Type: TABLE; Schema: ngcloud_ru; Owner: vc_billing
--
CREATE TABLE ngcloud_ru.capacity_resource (
vdc_id uuid NOT NULL,
"timestamp" timestamp with time zone NOT NULL,
cpu_allocated integer NOT NULL,
cpu_limit integer NOT NULL,
cpu_reserved integer NOT NULL,
cpu_used integer NOT NULL,
cpu_speed integer NOT NULL,
mem_allocated integer NOT NULL,
mem_limit integer NOT NULL,
mem_reserved integer NOT NULL,
mem_used integer NOT NULL
);
ALTER TABLE ngcloud_ru.capacity_resource OWNER TO vc_billing;
--
-- TOC entry 223 (class 1259 OID 1893993)
-- Name: capacity_storage; Type: TABLE; Schema: ngcloud_ru; Owner: vc_billing
--
CREATE TABLE ngcloud_ru.capacity_storage (
vdc_id uuid NOT NULL,
storage_profile_types_id integer NOT NULL,
"timestamp" timestamp with time zone NOT NULL,
id uuid NOT NULL,
"limit" integer NOT NULL,
used integer NOT NULL
);
ALTER TABLE ngcloud_ru.capacity_storage OWNER TO vc_billing;
--
-- TOC entry 220 (class 1259 OID 1893965)
-- Name: storage_profile_types; Type: TABLE; Schema: ngcloud_ru; Owner: vc_billing
--
CREATE TABLE ngcloud_ru.storage_profile_types (
id integer NOT NULL,
name character varying(255) NOT NULL
);
ALTER TABLE ngcloud_ru.storage_profile_types OWNER TO vc_billing;
--
-- TOC entry 219 (class 1259 OID 1893964)
-- Name: storage_profile_types_id_seq; Type: SEQUENCE; Schema: ngcloud_ru; Owner: vc_billing
--
CREATE SEQUENCE ngcloud_ru.storage_profile_types_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE ngcloud_ru.storage_profile_types_id_seq OWNER TO vc_billing;
--
-- TOC entry 3413 (class 0 OID 0)
-- Dependencies: 219
-- Name: storage_profile_types_id_seq; Type: SEQUENCE OWNED BY; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER SEQUENCE ngcloud_ru.storage_profile_types_id_seq OWNED BY ngcloud_ru.storage_profile_types.id;
--
-- TOC entry 218 (class 1259 OID 1893959)
-- Name: tenant; Type: TABLE; Schema: ngcloud_ru; Owner: vc_billing
--
CREATE TABLE ngcloud_ru.tenant (
id uuid NOT NULL,
name character varying(255) NOT NULL,
wzcode bigint
);
ALTER TABLE ngcloud_ru.tenant OWNER TO vc_billing;
--
-- TOC entry 221 (class 1259 OID 1893973)
-- Name: vdc; Type: TABLE; Schema: ngcloud_ru; Owner: vc_billing
--
CREATE TABLE ngcloud_ru.vdc (
id uuid NOT NULL,
tenant_id uuid NOT NULL,
name character varying(255) NOT NULL
);
ALTER TABLE ngcloud_ru.vdc OWNER TO vc_billing;
--
-- TOC entry 3245 (class 2604 OID 1893968)
-- Name: storage_profile_types id; Type: DEFAULT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.storage_profile_types ALTER COLUMN id SET DEFAULT nextval('ngcloud_ru.storage_profile_types_id_seq'::regclass);
--
-- TOC entry 3255 (class 2606 OID 1893987)
-- Name: capacity_resource capacity_resource_pkey; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.capacity_resource
ADD CONSTRAINT capacity_resource_pkey PRIMARY KEY (vdc_id, "timestamp");
--
-- TOC entry 3257 (class 2606 OID 1893997)
-- Name: capacity_storage capacity_storage_pkey; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.capacity_storage
ADD CONSTRAINT capacity_storage_pkey PRIMARY KEY (vdc_id, id, "timestamp");
--
-- TOC entry 3249 (class 2606 OID 1893972)
-- Name: storage_profile_types storage_profile_types_name_key; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.storage_profile_types
ADD CONSTRAINT storage_profile_types_name_key UNIQUE (name);
--
-- TOC entry 3251 (class 2606 OID 1893970)
-- Name: storage_profile_types storage_profile_types_pkey; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.storage_profile_types
ADD CONSTRAINT storage_profile_types_pkey PRIMARY KEY (id);
--
-- TOC entry 3247 (class 2606 OID 1893963)
-- Name: tenant tenant_pkey; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.tenant
ADD CONSTRAINT tenant_pkey PRIMARY KEY (id);
--
-- TOC entry 3253 (class 2606 OID 1893977)
-- Name: vdc vdc_pkey; Type: CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.vdc
ADD CONSTRAINT vdc_pkey PRIMARY KEY (id);
--
-- TOC entry 3259 (class 2606 OID 1893988)
-- Name: capacity_resource capacity_resource_vdc_id_fkey; Type: FK CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.capacity_resource
ADD CONSTRAINT capacity_resource_vdc_id_fkey FOREIGN KEY (vdc_id) REFERENCES ngcloud_ru.vdc(id) ON DELETE CASCADE;
--
-- TOC entry 3260 (class 2606 OID 1894003)
-- Name: capacity_storage capacity_storage_storage_profile_types_id_fkey; Type: FK CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.capacity_storage
ADD CONSTRAINT capacity_storage_storage_profile_types_id_fkey FOREIGN KEY (storage_profile_types_id) REFERENCES ngcloud_ru.storage_profile_types(id);
--
-- TOC entry 3261 (class 2606 OID 1893998)
-- Name: capacity_storage capacity_storage_vdc_id_fkey; Type: FK CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.capacity_storage
ADD CONSTRAINT capacity_storage_vdc_id_fkey FOREIGN KEY (vdc_id) REFERENCES ngcloud_ru.vdc(id) ON DELETE CASCADE;
--
-- TOC entry 3258 (class 2606 OID 1893978)
-- Name: vdc vdc_tenant_id_fkey; Type: FK CONSTRAINT; Schema: ngcloud_ru; Owner: vc_billing
--
ALTER TABLE ONLY ngcloud_ru.vdc
ADD CONSTRAINT vdc_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES ngcloud_ru.tenant(id);
--
-- TOC entry 3410 (class 0 OID 0)
-- Dependencies: 222
-- Name: TABLE capacity_resource; Type: ACL; Schema: ngcloud_ru; Owner: vc_billing
--
GRANT SELECT ON TABLE ngcloud_ru.capacity_resource TO grafanabilling;
--
-- TOC entry 3411 (class 0 OID 0)
-- Dependencies: 223
-- Name: TABLE capacity_storage; Type: ACL; Schema: ngcloud_ru; Owner: vc_billing
--
GRANT SELECT ON TABLE ngcloud_ru.capacity_storage TO grafanabilling;
--
-- TOC entry 3412 (class 0 OID 0)
-- Dependencies: 220
-- Name: TABLE storage_profile_types; Type: ACL; Schema: ngcloud_ru; Owner: vc_billing
--
GRANT SELECT ON TABLE ngcloud_ru.storage_profile_types TO grafanabilling;
--
-- TOC entry 3414 (class 0 OID 0)
-- Dependencies: 218
-- Name: TABLE tenant; Type: ACL; Schema: ngcloud_ru; Owner: vc_billing
--
GRANT SELECT ON TABLE ngcloud_ru.tenant TO grafanabilling;
--
-- TOC entry 3415 (class 0 OID 0)
-- Dependencies: 221
-- Name: TABLE vdc; Type: ACL; Schema: ngcloud_ru; Owner: vc_billing
--
GRANT SELECT ON TABLE ngcloud_ru.vdc TO grafanabilling;
-- Completed on 2025-09-15 09:53:18
--
-- PostgreSQL database dump complete
--
+128
View File
@@ -0,0 +1,128 @@
CREATE SCHEMA IF NOT EXISTS vmreports;
ALTER SCHEMA vmreports OWNER TO dbo;
GRANT USAGE ON SCHEMA vmreports TO nifi;
GRANT ALL ON ALL TABLES IN SCHEMA vmreports TO nifi;
GRANT ALL ON ALL SEQUENCES IN SCHEMA vmreports TO nifi;
--ALTER DEFAULT PRIVILEGES IN SCHEMA vmreports GRANT ALL ON TABLES TO nifi;
--ALTER DEFAULT PRIVILEGES IN SCHEMA vmreports GRANT ALL ON SEQUENCES TO nifi;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo, postgres IN SCHEMA vmreports GRANT ALL ON TABLES TO nifi;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo, postgres IN SCHEMA vmreports GRANT SELECT ON TABLES TO appserver;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo, postgres IN SCHEMA vmreports GRANT ALL ON SEQUENCES TO nifi;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo, postgres IN SCHEMA vmreports GRANT USAGE ON SEQUENCES TO appserver;
drop table if exists vmreports.host;
create table vmreports.host(
"Name" varchar(255) NULL,
"Model" varchar(255) NULL,
"Cluster" varchar(255) NULL,
"Cluster_v2" varchar(255) NULL,
"Maintenance" varchar(255) NULL,
"CPU_Cores" float NULL, -- per host, not per socket
"CPU_Sockets" float NULL, -- not used in caclulations
"CPU_Speed" float NULL,
"RAM_Total" float NULL,
"RAM_Used" float NULL,
"CPU_Used" float NULL,
"CPU_Used_AVG" float NULL,
"CPU_Contention_95" float NULL,
"HT_Active" boolean NULL,
"CPUtoVM" float NULL,
"RAMtoVM" float NULL,
"Object_UUID" uuid NOT NULL,
ts timestamptz NOT NULL
,CONSTRAINT PK_host PRIMARY KEY (ts, "Object_UUID")
);
--grant all on vmreports.host to nifi;
drop table if exists vmreports.compute;
create table vmreports.compute (
"Name" varchar(255) NULL,
"UUID_VM" varchar(255) NULL,
"VDC" varchar(255) NULL,
"Organization_UUID" uuid NULL,
"Organization" varchar(255) NULL,
"WZ" varchar(255) NULL,
"Cluster" varchar(255) NULL,
"Host" varchar(255) NULL,
"Host_Model" varchar(255) NULL,
"CPU_Model" varchar(255) NULL,
"Host_CPU_Cores" float NULL,
"Host_CPU_Sockets" float NULL,
"Host_CPU_Speed" float NULL,
"Host_RAM" float NULL,
"Host_CPU_Used" float NULL,
"Host_CPU_Used_Avg" float NULL,
"VDC_CPU_Reserv" float NULL,
"VDC_CPU_Speed" float NULL,
"VM_vCPU_count" float NULL,
"VM_CPU_Used" float NULL,
"VM_CPU_Used_AVG" float NULL,
"VM_CPU_Reserv_GHz" float NULL,
"VM_CPU_Reserv_perc" float NULL,
"VM_RAM_Allocated" float NULL,
"VM_RAM_Used" float NULL,
"VM_is_ON" float NULL,
--"Summary": false,
--"Grand Total": false,
--"Group UUID": null,
"Object_UUID" uuid NOT NULL,
ts timestamptz NOT NULL
,CONSTRAINT PK_compute PRIMARY KEY (ts, "Object_UUID")
);
--grant all on vmreports.compute to nifi;
drop table if exists vmreports.storage;
create table vmreports.storage (
"Name" varchar(255) NULL,
"VM_UUID" varchar(255) NULL,
"VDC" varchar(255) NULL,
"Organization_UUID" uuid NULL,
"Organization" varchar(255) NULL,
"WZ" varchar(255) NULL,
"Cluster" varchar(255) NULL,
"Disk" varchar(255) NULL,
"Storage_Policy" varchar(255) NULL,
"Disk_Allocated" float NULL,
"Disk_Used" float NULL,
"VM_is_ON" float NULL,
"Object_UUID" uuid NOT NULL,
ts timestamptz NOT NULL
,CONSTRAINT PK_storage PRIMARY KEY (ts, "Object_UUID")
);
--grant all on vmreports.storage to nifi;
drop table if exists vmreports.vm_gpu;
create table vmreports.vm_gpu (
"Name" varchar(255) NULL,
"ClientID" varchar(255) NULL, --WZ
"VCD" varchar(255) NULL, --vcloud
"Org" varchar(255) NULL,
"OrgVDC" varchar(255) NULL,
"vCenter" varchar(255) NULL,
"GPU" jsonb NULL,
"OrgStatus" varchar(255) NULL,
"Object_UUID" uuid NOT NULL,
ts timestamptz NOT NULL
,CONSTRAINT PK_vm_gpu PRIMARY KEY (ts, "Object_UUID")
);
--grant all on vmreports.vm_gpu to nifi;
DROP TABLE IF EXISTS vmreports.vm_power_state;
CREATE TABLE IF NOT EXISTS vmreports.vm_power_state
(
"Object_UUID" uuid NOT NULL,
ts timestamp with time zone NOT NULL,
value float,
CONSTRAINT PK_vm_power_state PRIMARY KEY ("Object_UUID", ts)
);
--grant all on vmreports.vm_power_state to nifi;
+204
View File
@@ -0,0 +1,204 @@
--
-- PostgreSQL database dump
--
--
-- TOC entry 10 (class 2615 OID 9816969)
-- Name: vmreports; Type: SCHEMA; Schema: -; Owner: dbo
--
CREATE SCHEMA IF NOT EXISTS vmreports;
ALTER SCHEMA vmreports OWNER TO dbo;
GRANT USAGE ON SCHEMA vmreports TO nifi,appserver;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT SELECT ON TABLES TO appserver;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT ALL ON TABLES TO nifi;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT USAGE ON SEQUENCES TO nifi, appserver;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- TOC entry 721 (class 1259 OID 9816992)
-- Name: compute; Type: TABLE; Schema: vmreports; Owner: smishchuk
--
CREATE TABLE vmreports.compute (
"Name" character varying(255),
"UUID_VM" character varying(255),
"VDC" character varying(255),
"Organization_UUID" uuid,
"Organization" character varying(255),
"WZ" character varying(255),
"Cluster" character varying(255),
"Host" character varying(255),
"Host_Model" character varying(255),
"CPU_Model" character varying(255),
"Host_CPU_Cores" double precision,
"Host_CPU_Sockets" double precision,
"Host_CPU_Speed" double precision,
"Host_RAM" double precision,
"Host_CPU_Used" double precision,
"Host_CPU_Used_Avg" double precision,
"VDC_CPU_Reserv" double precision,
"VDC_CPU_Speed" double precision,
"VM_vCPU_count" double precision,
"VM_CPU_Used" double precision,
"VM_CPU_Used_AVG" double precision,
"VM_CPU_Reserv_GHz" double precision,
"VM_CPU_Reserv_perc" double precision,
"VM_RAM_Allocated" double precision,
"VM_RAM_Used" double precision,
"VM_is_ON" double precision,
"Object_UUID" uuid NOT NULL,
ts timestamp with time zone NOT NULL
);
ALTER TABLE vmreports.compute OWNER TO dbo;
--
-- TOC entry 722 (class 1259 OID 9817004)
-- Name: storage; Type: TABLE; Schema: vmreports; Owner: smishchuk
--
CREATE TABLE vmreports.storage (
"Name" character varying(255),
"VM_UUID" character varying(255),
"VDC" character varying(255),
"Organization_UUID" uuid,
"Organization" character varying(255),
"WZ" character varying(255),
"Cluster" character varying(255),
"Disk" character varying(255),
"Storage_Policy" character varying(255),
"Disk_Allocated" double precision,
"Disk_Used" double precision,
"VM_is_ON" double precision,
"Object_UUID" uuid NOT NULL,
ts timestamp with time zone NOT NULL
);
ALTER TABLE vmreports.storage OWNER TO dbo;
--
-- TOC entry 720 (class 1259 OID 9816984)
-- Name: host; Type: TABLE; Schema: vmreports; Owner: smishchuk
--
CREATE TABLE vmreports.host (
"Name" character varying(255),
"Model" character varying(255),
"Cluster" character varying(255),
"Maintenance" character varying(255),
"CPU_Cores" double precision,
"CPU_Sockets" double precision,
"CPU_Speed" double precision,
"RAM_Total" double precision,
"RAM_Used" double precision,
"CPU_Used" double precision,
"CPU_Used_AVG" double precision,
"CPU_Contention_95" double precision,
"Object_UUID" uuid NOT NULL,
ts timestamp with time zone NOT NULL,
"Cluster_v2" character varying(255),
"HT_Active" boolean,
"CPUtoVM" double precision,
"RAMtoVM" double precision
);
ALTER TABLE vmreports.host OWNER TO dbo;
--
-- TOC entry 733 (class 1259 OID 11093092)
-- Name: vm_gpu; Type: TABLE; Schema: vmreports; Owner: smishchuk
--
CREATE TABLE vmreports.vm_gpu (
"Name" character varying(255),
"ClientID" character varying(255),
"VCD" character varying(255),
"Org" character varying(255),
"OrgVDC" character varying(255),
"vCenter" character varying(255),
"GPU" jsonb,
"OrgStatus" character varying(255),
"Object_UUID" uuid NOT NULL,
ts timestamp with time zone NOT NULL
);
ALTER TABLE vmreports.vm_gpu OWNER TO dbo;
--
-- TOC entry 732 (class 1259 OID 11093087)
-- Name: vm_power_state; Type: TABLE; Schema: vmreports; Owner: smishchuk
--
CREATE TABLE vmreports.vm_power_state (
"Object_UUID" uuid NOT NULL,
ts timestamp with time zone NOT NULL,
value double precision
);
ALTER TABLE vmreports.vm_power_state OWNER TO dbo;
--
-- TOC entry 4761 (class 2606 OID 9816998)
-- Name: compute pk_compute; Type: CONSTRAINT; Schema: vmreports; Owner: smishchuk
--
ALTER TABLE ONLY vmreports.compute
ADD CONSTRAINT pk_compute PRIMARY KEY (ts, "Object_UUID");
--
-- TOC entry 4759 (class 2606 OID 9816990)
-- Name: host pk_host; Type: CONSTRAINT; Schema: vmreports; Owner: smishchuk
--
ALTER TABLE ONLY vmreports.host
ADD CONSTRAINT pk_host PRIMARY KEY (ts, "Object_UUID");
--
-- TOC entry 4763 (class 2606 OID 9817010)
-- Name: storage pk_storage; Type: CONSTRAINT; Schema: vmreports; Owner: smishchuk
--
ALTER TABLE ONLY vmreports.storage
ADD CONSTRAINT pk_storage PRIMARY KEY (ts, "Object_UUID");
--
-- TOC entry 4767 (class 2606 OID 11093098)
-- Name: vm_gpu pk_vm_gpu; Type: CONSTRAINT; Schema: vmreports; Owner: smishchuk
--
ALTER TABLE ONLY vmreports.vm_gpu
ADD CONSTRAINT pk_vm_gpu PRIMARY KEY (ts, "Object_UUID");
--
-- TOC entry 4765 (class 2606 OID 11093091)
-- Name: vm_power_state pk_vm_power_state; Type: CONSTRAINT; Schema: vmreports; Owner: smishchuk
--
ALTER TABLE ONLY vmreports.vm_power_state
ADD CONSTRAINT pk_vm_power_state PRIMARY KEY ("Object_UUID", ts);
--
-- TOC entry 5168 (class 0 OID 0)
-- Dependencies: 10
-- Name: SCHEMA vmreports; Type: ACL; Schema: -; Owner: dbo
--
GRANT ALL ON SCHEMA vmreports TO nifi;
+9
View File
@@ -0,0 +1,9 @@
CREATE INDEX ON ngcloud_ru.capacity_resource (timestamp);
CREATE INDEX ON ngcloud_ru.capacity_storage (timestamp);
CREATE INDEX ON s3billing.bucket_stat (timestamp_addition);
CREATE INDEX ON s3billing.usage_bucket_by_user (time);
CREATE INDEX ON gpu.vm (dt_load);
CREATE INDEX ON gpu.vm_power_state (ts);
+101
View File
@@ -0,0 +1,101 @@
ALTER TABLE IF EXISTS vmreports.storage
OWNER to dbo;
REVOKE ALL ON TABLE vmreports.storage FROM analyst;
REVOKE ALL ON TABLE vmreports.storage FROM analytics;
REVOKE ALL ON TABLE vmreports.storage FROM appserver;
GRANT SELECT ON TABLE vmreports.storage TO analyst;
GRANT SELECT ON TABLE vmreports.storage TO analytics;
GRANT SELECT ON TABLE vmreports.storage TO appserver;
GRANT ALL ON TABLE vmreports.storage TO dbo;
GRANT ALL ON TABLE vmreports.storage TO designer;
GRANT ALL ON TABLE vmreports.storage TO nifi;
---------------
ALTER TABLE IF EXISTS vmreports.compute
OWNER to dbo;
REVOKE ALL ON TABLE vmreports.compute FROM analyst;
REVOKE ALL ON TABLE vmreports.compute FROM analytics;
REVOKE ALL ON TABLE vmreports.compute FROM appserver;
GRANT SELECT ON TABLE vmreports.compute TO analyst;
GRANT SELECT ON TABLE vmreports.compute TO analytics;
GRANT SELECT ON TABLE vmreports.compute TO appserver;
GRANT ALL ON TABLE vmreports.compute TO dbo;
GRANT ALL ON TABLE vmreports.compute TO designer;
GRANT ALL ON TABLE vmreports.compute TO nifi;
--------------------
ALTER TABLE IF EXISTS vmreports.host
OWNER to dbo;
REVOKE ALL ON TABLE vmreports.host FROM analyst;
REVOKE ALL ON TABLE vmreports.host FROM analytics;
REVOKE ALL ON TABLE vmreports.host FROM appserver;
GRANT SELECT ON TABLE vmreports.host TO analyst;
GRANT SELECT ON TABLE vmreports.host TO analytics;
GRANT SELECT ON TABLE vmreports.host TO appserver;
GRANT ALL ON TABLE vmreports.host TO dbo;
GRANT ALL ON TABLE vmreports.host TO designer;
GRANT ALL ON TABLE vmreports.host TO nifi;
------------------------------------
ALTER TABLE IF EXISTS vmreports.vm_gpu
OWNER to dbo;
REVOKE ALL ON TABLE vmreports.vm_gpu FROM analyst;
REVOKE ALL ON TABLE vmreports.vm_gpu FROM analytics;
REVOKE ALL ON TABLE vmreports.vm_gpu FROM appserver;
GRANT SELECT ON TABLE vmreports.vm_gpu TO analyst;
GRANT SELECT ON TABLE vmreports.vm_gpu TO analytics;
GRANT SELECT ON TABLE vmreports.vm_gpu TO appserver;
GRANT ALL ON TABLE vmreports.vm_gpu TO dbo;
GRANT ALL ON TABLE vmreports.vm_gpu TO designer;
GRANT ALL ON TABLE vmreports.vm_gpu TO nifi;
-----------------
ALTER TABLE IF EXISTS vmreports.vm_power_state
OWNER to dbo;
REVOKE ALL ON TABLE vmreports.vm_power_state FROM analyst;
REVOKE ALL ON TABLE vmreports.vm_power_state FROM analytics;
REVOKE ALL ON TABLE vmreports.vm_power_state FROM appserver;
GRANT SELECT ON TABLE vmreports.vm_power_state TO analyst;
GRANT SELECT ON TABLE vmreports.vm_power_state TO analytics;
GRANT SELECT ON TABLE vmreports.vm_power_state TO appserver;
GRANT ALL ON TABLE vmreports.vm_power_state TO dbo;
GRANT ALL ON TABLE vmreports.vm_power_state TO designer;
GRANT ALL ON TABLE vmreports.vm_power_state TO nifi;
+65
View File
@@ -0,0 +1,65 @@
SET ROLE dbo;
DROP SCHEMA IF EXISTS vmreports CASCADE;
CREATE SCHEMA IF NOT EXISTS vmreports AUTHORIZATION dbo;
GRANT USAGE ON SCHEMA vmreports TO appserver;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT SELECT ON TABLES TO appserver;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT USAGE ON SEQUENCES TO appserver;
GRANT USAGE ON SCHEMA vmreports TO nifi,analytics,analyst;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT SELECT ON TABLES TO nifi,analytics,analyst;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT USAGE ON SEQUENCES TO nifi,analytics,analyst;
/*
конструкция осложняется тем, что
нужен один пользователь-владелец и второй читатель
Это вызывает множество телодвижений при создании.
Необходимость указывать пароль при локальном коннекте не очень радует
(ведь пользователь локальный, прописан на этом же сервере)
*/
--DROP FOREIGN TABLE IF EXISTS host;
--CREATE FOREIGN TABLE vmreports.host () SERVER billing OPTIONS (schema_name 'vmreports', table_name 'host'); -- вместо этого лучше импортировать схему
DROP SERVER IF EXISTS billing CASCADE;
CREATE SERVER billing
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'localhost', port '5432', dbname 'billing', updatable 'false');
-- для импорта схемы мы создаем маппинг владельцу
DROP USER MAPPING IF EXISTS FOR PUBLIC SERVER billing;
CREATE USER MAPPING FOR PUBLIC SERVER billing OPTIONS (user 'appserver', password '***');
IMPORT FOREIGN SCHEMA vmreports FROM SERVER billing INTO vmreports;
--GRANT SELECT ON vmreports.host to appserver;
--DROP USER MAPPING IF EXISTS FOR dbo SERVER billing; -- мы импортировали схему, маппинг больше не нужен.
-- Но нам все равно пришлось указать пароль дважды
--DROP USER MAPPING IF EXISTS FOR appserver SERVER billing;
--CREATE USER MAPPING FOR appserver SERVER billing OPTIONS (user 'nifi', password '***');
--проверка
--set role appserver; select * from vmreports.host;
-- это если интересно, не разрешен ли локальный коннект
--SELECT * FROM pg_hba_file_rules;
/*
Довольно тонкий нюанс, легко ускользающий
ALTER DEFAULT PRIVILEGES FOR ROLE target_creator_role
IN SCHEMA schema_name
GRANT privilege_type ON TABLES TO recipient_role;
Recommended Strategy for "Any User"Because there is no "FOR ALL ROLES" option, the best practice is to have all users create objects as a shared group role.Create a group role (e.g., schema_owner_role).Grant that role to all relevant users.Instruct users to run SET ROLE schema_owner_role; before creating objects.Run ALTER DEFAULT PRIVILEGES FOR ROLE schema_owner_role ... once to cover all objects created under that role.
*/
/*
Best Practices for Role Owners:Dedicated Schema Owner: Create a dedicated, non-login role for managing database schema objects (DDL) and make it the owner of the database/schemas.Separation of Duties: Separate the application user (DML - SELECT, INSERT, UPDATE) from the owner role (DDL - CREATE, ALTER, DROP).Use ALTER DEFAULT PRIVILEGES: Set up default privileges for new objects to ensure that user roles automatically gain access to new tables or functions created by the owner role.Leverage Role Hierarchy: Create group roles (e.g., app_reader, app_writer) and grant these to individual user roles for easier permission management.Avoid Public Schema: Create specific schemas and revoke CREATE privileges on the public schema to prevent accidental object creation.Secure SUPERUSER Usage: Only use the default postgres superuser role for initial setup and critical administrative tasks. Never use it for applications.Example Implementation:sql-- Create a schema owner
CREATE ROLE ddl_user NOLOGIN;
-- Create an app user
CREATE ROLE dml_user LOGIN PASSWORD 'secure_password';
-- Assign ownership
ALTER DATABASE mydb OWNER TO ddl_user;
ALTER SCHEMA public OWNER TO ddl_user;
-- Grant future access
ALTER DEFAULT PRIVILEGES FOR ROLE ddl_user IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO dml_user;
*/
+37
View File
@@ -0,0 +1,37 @@
CREATE EXTENSION IF NOT EXISTS postgres_fdw;
GRANT USAGE ON FOREIGN DATA WRAPPER postgres_fdw TO dbo;
SET ROLE dbo;
DROP SCHEMA IF EXISTS vmreports CASCADE;
CREATE SCHEMA IF NOT EXISTS vmreports AUTHORIZATION dbo;
GRANT USAGE ON SCHEMA vmreports TO nifi,analytics,analyst;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT SELECT ON TABLES TO nifi,analytics,analyst; -- это работает и для внешних таблиц
--ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT USAGE ON SEQUENCES TO nifi,analytics,analyst;
/*
конструкция осложняется тем, что
нужен один пользователь-владелец и второй читатель
Это вызывает множество телодвижений при создании.
Необходимость указывать пароль при локальном коннекте не очень радует
(ведь пользователь локальный, прописан на этом же сервере)
*/
DROP SERVER IF EXISTS billing CASCADE;
CREATE SERVER billing
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'localhost', port '5432', dbname 'billing', updatable 'false');
DROP USER MAPPING IF EXISTS FOR PUBLIC SERVER billing;
CREATE USER MAPPING FOR PUBLIC SERVER billing OPTIONS (user 'nifi', password '***');
IMPORT FOREIGN SCHEMA vmreports FROM SERVER billing INTO vmreports;
--select * from vmreports.host;
/*
план миграции
Забекапить схему vmreports
Восстановить схему vmreports в базу биллинг
Поменять целевую базу импорта НиФи (для прода - втащить новый обмен из репозитория)
Выполнить скрипт в базе DWH - он удалит физические таблицы и создаст внешние таблицы на базу billing
*/
+113
View File
@@ -0,0 +1,113 @@
-- выполнить в базе dwh для создания внешних таблиц схемы vmreports
-- перед выполнением забекапить схему vmreports для восстановления в БД billing
CREATE EXTENSION IF NOT EXISTS postgres_fdw;
GRANT USAGE ON FOREIGN DATA WRAPPER postgres_fdw TO dbo;
SET ROLE dbo;
--DROP SCHEMA IF EXISTS vmreports CASCADE;
CREATE SCHEMA IF NOT EXISTS vmreports AUTHORIZATION dbo;
GRANT USAGE ON SCHEMA vmreports TO nifi,analytics,analyst;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT SELECT ON TABLES TO nifi,analytics,analyst; -- это работает и для внешних таблиц
--ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT USAGE ON SEQUENCES TO nifi,analytics,analyst;
/*
конструкция осложняется тем, что
нужен один пользователь-владелец и второй читатель
Это вызывает множество телодвижений при создании.
Необходимость указывать пароль при локальном коннекте не очень радует
(ведь пользователь локальный, прописан на этом же сервере)
А при смене его пароля все сломается, и поиск ошибки будет неочевиден
*/
DO $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN
SELECT tablename
FROM pg_tables
WHERE schemaname = 'vmreports'
LOOP
EXECUTE format('ALTER TABLE vmreports.%I RENAME TO %I',
rec.tablename,
'del_' || rec.tablename);
END LOOP;
END $$;
DROP SERVER IF EXISTS billing CASCADE;
CREATE SERVER billing
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'localhost', port '5432', dbname 'billing', updatable 'false');
DROP USER MAPPING IF EXISTS FOR PUBLIC SERVER billing;
CREATE USER MAPPING FOR PUBLIC SERVER billing OPTIONS (user 'nifi', password '***');
IMPORT FOREIGN SCHEMA vmreports FROM SERVER billing INTO vmreports;
--select * from vmreports.host;
/*
план миграции - черновик
Забекапить схему vmreports
Восстановить схему vmreports из бэкапа в базу биллинг
Поменять целевую базу импорта НиФи (для прода - втащить новый обмен из репозитория)
Выполнить скрипт в базе DWH - он удалит (переименует) физические таблицы и создаст внешние таблицы на базу billing
*/
/*
Исходно данные host, compute, storage, (gpu)
загружались в dwh.vmreports,
для анализа capacity
В текущей конфигурации,
чтобы избежать дублирования
таблиц, данных и обменов NiFi
Направляем загрузку данных из VROPS в схему billing.vmreports
и в базе dwh (схема vmreports) создаем Foreign Tables на billing.vmreports.
-----------------------------------------------
Миграция на проде:
убедиться, что новый обмен VROPS->billing.vmreports (billing/vmReports) загружен в NiFi Registry
найти пароль nifi (под ним коннект к localhost)
остановить загрузку из VROPS на nifi-01 (billing/vmReports),
сделать бэкап схемы dwh.vmreports (займет минут 10 ... заняло 215 сек),
Восстановить схему vmreports в БД billing - запустить восстановление (Clean Before Restore) (РИСК: восстановление может занять много времени и завершиться с ошибками, обычно так бывает)
select count (*) from vmreports.storage
Выполнить этот скрипт (заменив пароль nifi на актуальный перед выполнением)
переименовать таблицы из схемы vmreports для удаления (есть в скрипте выше)
создать Foreign Tables (коннект проверится при импорте схемы)
Проверить, что права назначились коррректно
Обновить обмен billing/vmReports на продуктиве, запустить
-----------------------------------------------------------------
Данные из billing.vmreports также реплицируются
(процессорами группы LK)
в буферную базу rpt
*/
/*
17:04:49 MSKERROR0dff1f93-153d-3a90-ed64-55d23960b88c
EvaluateJsonPath[id=0dff1f93-153d-3a90-ed64-55d23960b88c] FlowFile FlowFile[filename=06c68bec-d824-4114-913b-bda34cb4fb1d] did not have valid JSON content.
-- со второй попытки отработало
17:05:15 MSKERROR239db407-76f2-3660-7fad-c4bedff6a4f8
InvokeHTTP[id=239db407-76f2-3660-7fad-c4bedff6a4f8] Request Processing failed: FlowFile[filename=3474c340-f360-4a1e-a27e-283be41fc264]: java.net.SocketTimeoutException: timeout
- Caused by: java.net.SocketException: Socket closed
https://payg.adl.nubes.ru/payg.cfm с первого раза таймаут, со второго посчиталось
*/
/*
теперь ищу в нифи примеры с кроном
17:25:05 MSKERROR16efa0f6-f1e7-3755-04fc-5be0383244ec
PutDatabaseRecord[id=16efa0f6-f1e7-3755-04fc-5be0383244ec] Failed to put Records to database for FlowFile[filename=11f8176f-2a98-4199-932e-22e2fc2073b1]. Routing to failure.: org.apache.nifi.serialization.MalformedRecordException: Record does not have a value for the UpdateKey column 'obj_uuid'
17:25:05 MSKERROR16efa0f6-f1e7-3755-04fc-5be0383244ec
PutDatabaseRecord[id=16efa0f6-f1e7-3755-04fc-5be0383244ec] Record does not have a value for the UpdateKey column 'obj_uuid'
17:25:05 MSKERROR16efa0f6-f1e7-3755-04fc-5be0383244ec
PutDatabaseRecord[id=16efa0f6-f1e7-3755-04fc-5be0383244ec] Failed to put Records to database for FlowFile[filename=5039a3b6-2244-4451-81fc-deb8074f3cc7]. Routing to failure.: org.apache.nifi.serialization.MalformedRecordException: Record does not have a value for the UpdateKey column 'obj_uuid'
17:25:05 MSKERROR16efa0f6-f1e7-3755-04fc-5be0383244ec
PutDatabaseRecord[id=16efa0f6-f1e7-3755-04fc-5be0383244ec] Record does not have a value for the UpdateKey column 'obj_uuid'
17:25:05 MSKERROR16efa0f6-f1e7-3755-04fc-5be0383244ec
PutDatabaseRecord[id=16efa0f6-f1e7-3755-04fc-5be0383244ec] Failed to put Records to database for FlowFile[filename=8c6e9efb-198d-4303-897a-53fcfdf4d0f6]. Routing to failure.: org.apache.nifi.serialization.MalformedRecordException: Record does not have a value for the UpdateKey column 'obj_uuid'
*/
+47
View File
@@ -0,0 +1,47 @@
SET ROLE dbo;
DROP SCHEMA IF EXISTS vmreports CASCADE;
CREATE SCHEMA IF NOT EXISTS vmreports AUTHORIZATION dbo;
--GRANT ALL ON SCHEMA vmreports TO postgres;
BEGIN
GRANT USAGE ON SCHEMA vmreports TO appserver;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT SELECT ON TABLES TO appserver;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT USAGE ON SEQUENCES TO appserver;
EXCEPTION END;
BEGIN
GRANT USAGE ON SCHEMA vmreports TO nifi,analytics,analyst;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT SELECT ON TABLES TO nifi,analytics,analyst;
ALTER DEFAULT PRIVILEGES FOR ROLE dbo IN SCHEMA vmreports GRANT USAGE ON SEQUENCES TO nifi,analytics,analyst;
EXCEPTION WHEN others THEN END;
/*
конструкция осложняется тем, что
нужен один пользователь-владелец и второй читатель
Это вызывает множество телодвижений при создании.
Необходимость указывать пароль при локальном коннекте не очень радует
(ведь пользователь локальный, прописан на этом же сервере)
*/
--DROP FOREIGN TABLE IF EXISTS host;
--CREATE FOREIGN TABLE vmreports.host () SERVER billing OPTIONS (schema_name 'vmreports', table_name 'host'); -- вместо этого лучше импортировать схему
DROP SERVER IF EXISTS billing CASCADE;
CREATE SERVER billing
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'localhost', port '5432', dbname 'billing');
-- для импорта схемы мы создаем маппинг владельцу
DROP USER MAPPING IF EXISTS FOR dbo SERVER billing;
CREATE USER MAPPING FOR dbo SERVER billing OPTIONS (user 'appserver', password '***');
IMPORT FOREIGN SCHEMA vmreports FROM SERVER billing INTO vmreports;
GRANT SELECT ON vmreports.host to appserver;
DROP USER MAPPING IF EXISTS FOR dbo SERVER billing; -- мы импортировали схему, маппинг больше не нужен.
-- Но нам все равно пришлось указать пароль дважды
DROP USER MAPPING IF EXISTS FOR appserver SERVER billing;
CREATE USER MAPPING FOR appserver SERVER billing OPTIONS (user 'appserver', password '***');
--проверка
--set role appserver; select * from vmreports.host;
-- это если интересно, не разрешен ли локальный коннект
--SELECT * FROM pg_hba_file_rules;
+52
View File
@@ -0,0 +1,52 @@
SET ROLE dbo;
DROP TABLE IF EXISTS vmreports.vsan_storage CASCADE;
CREATE TABLE vmreports.vsan_storage (
"Name" varchar(255) NULL,
"ClusterNew" varchar(255) NULL,
"Total" float NULL,
"Usable" float NULL,
"Used" float NULL,
"EffectiveFree" float NULL,
"Object_UUID" uuid NOT NULL,
ts timestamptz NOT NULL
,CONSTRAINT PK_vsan_storage PRIMARY KEY (ts, "Object_UUID")
);
--ALTER TABLE vmreports.vsan_storage OWNER TO dbo;
GRANT ALL ON vmreports.vsan_storage TO nifi;
--select * from vmreports.vsan_storage;
/*
{
"type": "record",
"name": "vmreports.vsan_storage",
"namespace": "org.apache.nifi",
"fields": [
{"name":"Object_UUID", "type":{"type": "string", "logicalType": "uuid"}},
{"name":"ts","type":{"type": "long", "logicalType": "local-timestamp-millis"}},
{"name":"Name", "type":["string" ,"null"]},
{"name":"ClusterNew", "type":["string" ,"null"]},
{"name":"Total", "type":["float" ,"null"]},
{"name":"Usable", "type":["float" ,"null"]},
{"name":"Used", "type":["float" ,"null"]},
{"name":"EffectiveFree", "type":["float" ,"null"]}
]
}
*/
/*
{
"*": {
"objId":"[&1].Name",
"1":"[&1].ClusterNew",
"2":"[&1].Total",
"3":"[&1].Usable",
"4":"[&1].Used",
"5":"[&1].EffectiveFree",
"objUUID":"[&1].Object_UUID"
}
}
*/
-8
View File
@@ -1,8 +0,0 @@
{
"ds_rpt_class": "org.postgresql.Driver",
"ds_rpt_connectionString": "jdbc:postgresql://db-services.adl.nubes.ru:5432/dwh",
"ds_rpt_username": "smishchuk",
"ds_rpt_password": "encrypted:46692f9b4e3bda4931a9b83685a04a15187cb725295dc68627d7eb0b760362bc8a9de8d28395fca70b30fa4595cf3ed4a83772ee73eaf13a",
"ds_rpt_connectionLimit": "5",
"IDP_CLIENT_SECRET": "mXuJehst7ZZSBIFpjpLZFicYRhpLmxVs",
}
+633
View File
@@ -0,0 +1,633 @@
{
"companyId": "1",
"contracts": [
{
"contractId": "01983721-6049-72dd-a682-a5af1bd72420",
"contractInsideId": "02319_1",
"contractOutsideId": "02319_1",
"contractStartDate": "20250801",
"isContractTerminated": false,
"CalculationPeriod": "20260701",
"additionalAgreements": [
{
"additionalAgreementId": "019b6ec1-1984-78fc-9e48-0984aa1f3ca7",
"additionalAgreementIndex": 3,
"additionalAgreementName": "Доп соглашение 3, к сделке №3768 Сделка на изменения договора",
"additionalAgreementStartDate": "",
"additionalAgreementBaseBy": "019a5ef8-eebe-76b3-acb9-b5c37748f4ef",
"isActual": true,
"CalculationPeriod": "20260701",
"services": [
{
"components": [
{
"paramId": 118,
"componentCode": "paas.s3.ceph.vol",
"component": "Дисковое пространство: Объем хранения",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.8300000000000001,
"discount": 0,
"atomicCode": "paas.s3.ceph.vol"
},
{
"paramId": 69,
"componentCode": "paas.s3.ceph.trf",
"component": "Исходящий трафик",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.5249999999999999,
"discount": 0,
"atomicCode": "paas.s3.ceph.trf"
},
{
"paramId": 68,
"componentCode": "paas.s3.ceph.get",
"component": "Запросы GET, HEAD, OPTIONS",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 0.3538,
"discount": 0,
"atomicCode": "paas.s3.ceph.get"
},
{
"paramId": 70,
"componentCode": "paas.s3.ceph.put",
"component": "Запросы PUT, POST, PATCH, LIST",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 4.5750000000000002,
"discount": 0,
"atomicCode": "paas.s3.ceph.put"
}
],
"staticId": "e57bb0bd-8946-4603-95e8-c9c331378bce",
"serviceId": "2e1681d7-542e-489c-9169-de562bdca83a",
"serviceHash": "e57bb0bd-8946-4603-95e8-c9c331378bce3",
"LineCRMID": "019b6ec6-a779-7902-abeb-d2f7e746d64e",
"serviceCode": "paas.s3.ceph",
"endCustomerName": "",
"endCustomerINN": "",
"catalogName": "Объектное хранилище",
"discount": 0.24125452,
"quantity": 1,
"dateStartProvision": "20260101",
"dateEndProvision": "",
"serviceStart": {
"currentTimeZoneName": "UTC",
"ts": "2026-01-01T00:00:00.000Z"
},
"serviceCustomName": "Объектное хранилище: S3 CEPH, в составе:\n Дисковое пространство: Объем хранения - 1 ГБайт\nИсходящий трафик - 1 ГБайт\nЗапросы GET, HEAD, OPTIONS - 1 10тыс.шт.\nЗапросы PUT, POST, PATCH, LIST - 1 10тыс.шт.\n",
"serviceType": 3,
"service_is_easy": false,
"vat": 22,
"vatRate": 0.22,
"vatPerc": 22,
"vatFree": 0,
"priceWVat": "",
"serviceVersionGuid": "2e1681d7-542e-489c-9169-de562bdca83a",
"dealIndex": 3768,
"CalculationPeriod": "20260701"
},
{
"components": [
{
"paramId": 118,
"componentCode": "paas.s3.cphc.vol",
"component": "Дисковое пространство: Объем хранения",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.026833333333,
"discount": 0,
"atomicCode": "paas.s3.cphc.vol"
},
{
"paramId": 69,
"componentCode": "paas.s3.cphc.trf",
"component": "Исходящий трафик",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.5249999999999999,
"discount": 0,
"atomicCode": "paas.s3.cphc.trf"
},
{
"paramId": 68,
"componentCode": "paas.s3.cphc.get",
"component": "Запросы GET, HEAD, OPTIONS",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 0.97599999999999998,
"discount": 0,
"atomicCode": "paas.s3.cphc.get"
},
{
"paramId": 70,
"componentCode": "paas.s3.cphc.put",
"component": "Запросы PUT, POST, PATCH, LIST",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 11.834,
"discount": 0,
"atomicCode": "paas.s3.cphc.put"
}
],
"staticId": "2031f3ac-e921-4816-b91f-9b5b1cf5bf94",
"serviceId": "5271dc39-2ec5-4f20-b91e-fdffb4f363b9",
"serviceHash": "2031f3ac-e921-4816-b91f-9b5b1cf5bf943",
"LineCRMID": "019b6ec6-a82c-7902-93a3-48ba4e25d71d",
"serviceCode": "paas.s3.cphc",
"endCustomerName": "",
"endCustomerINN": "",
"catalogName": "Объектное хранилище",
"discount": 0,
"quantity": 1,
"dateStartProvision": "20260101",
"dateEndProvision": "",
"serviceStart": {
"currentTimeZoneName": "UTC",
"ts": "2026-01-01T00:00:00.000Z"
},
"serviceCustomName": "Объектное хранилище: S3 CEPH Холодное, в составе:\n Дисковое пространство: Объем хранения - 1 ГБайт\nИсходящий трафик - 1 ГБайт\nЗапросы GET, HEAD, OPTIONS - 1 10тыс.шт.\nЗапросы PUT, POST, PATCH, LIST - 1 10тыс.шт.\n",
"serviceType": 3,
"service_is_easy": false,
"vat": 22,
"vatRate": 0.22,
"vatPerc": 22,
"vatFree": 0,
"priceWVat": "",
"serviceVersionGuid": "5271dc39-2ec5-4f20-b91e-fdffb4f363b9",
"dealIndex": 3768,
"CalculationPeriod": "20260701"
},
{
"components": [
{
"paramId": 175,
"componentCode": "paas.s3.cphc.volq",
"component": "Дисковое пространство: Квота",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 2.7999999999999998,
"discount": 0,
"atomicCode": "paas.s3.cphc.volq"
}
],
"staticId": "f216f0ac-6c61-42aa-a454-56a93c707d11",
"serviceId": "776a3490-cb3a-4721-b521-7bf97073e104",
"serviceHash": "f216f0ac-6c61-42aa-a454-56a93c707d112",
"LineCRMID": "019b6ec6-a86d-7902-9ba2-defe46f2a036",
"serviceCode": "paas.s3.cphc",
"endCustomerName": "",
"endCustomerINN": "",
"catalogName": "Объектное хранилище",
"discount": 63.928570999999998,
"quantity": 1000,
"dateStartProvision": "20250826",
"dateEndProvision": "20250930",
"serviceStart": {
"currentTimeZoneName": "UTC",
"ts": "2025-08-26T00:00:00.000Z"
},
"serviceEnd": {
"currentTimeZoneName": "UTC",
"ts": "2025-09-30T00:00:00.000Z"
},
"serviceCustomName": "Объектное хранилище: S3 CEPH Холодное (Квота), в составе:\n Дисковое пространство: Квота - 1 ГБайт\n",
"serviceType": 2,
"service_is_easy": false,
"vat": 20,
"vatRate": 0.20000000000000001,
"vatPerc": 20,
"vatFree": 0,
"priceWVat": "",
"serviceVersionGuid": "776a3490-cb3a-4721-b521-7bf97073e104",
"dealIndex": 3768,
"CalculationPeriod": "20260701"
},
{
"components": [
{
"paramId": 175,
"componentCode": "paas.s3.cphc.volq",
"component": "Дисковое пространство: Квота",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 2.7999999999999998,
"discount": 0,
"atomicCode": "paas.s3.cphc.volq"
}
],
"staticId": "78707549-a675-46e0-bc5b-790ef4d21b6d",
"serviceId": "2e9369bc-7f3e-4c31-bb72-3d19e979964e",
"serviceHash": "78707549-a675-46e0-bc5b-790ef4d21b6d2",
"LineCRMID": "019b6ec6-a8ab-7902-af73-1034bf013a16",
"serviceCode": "paas.s3.cphc",
"endCustomerName": "",
"endCustomerINN": "",
"catalogName": "Объектное хранилище",
"discount": 63.928570999999998,
"quantity": 40000,
"dateStartProvision": "20250826",
"dateEndProvision": "20250930",
"serviceStart": {
"currentTimeZoneName": "UTC",
"ts": "2025-08-26T00:00:00.000Z"
},
"serviceEnd": {
"currentTimeZoneName": "UTC",
"ts": "2025-09-30T00:00:00.000Z"
},
"serviceCustomName": "Объектное хранилище: S3 CEPH Холодное (Квота), в составе:\n Дисковое пространство: Квота - 1 ГБайт\n",
"serviceType": 2,
"service_is_easy": false,
"vat": 20,
"vatRate": 0.20000000000000001,
"vatPerc": 20,
"vatFree": 0,
"priceWVat": "",
"serviceVersionGuid": "2e9369bc-7f3e-4c31-bb72-3d19e979964e",
"dealIndex": 3768,
"CalculationPeriod": "20260701"
},
{
"components": [
{
"paramId": 118,
"componentCode": "paas.s3.cphc.vol",
"component": "Дисковое пространство: Объем хранения",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.01,
"discount": 0,
"atomicCode": "paas.s3.cphc.vol"
},
{
"paramId": 69,
"componentCode": "paas.s3.cphc.trf",
"component": "Исходящий трафик",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.5,
"discount": 0,
"atomicCode": "paas.s3.cphc.trf"
},
{
"paramId": 68,
"componentCode": "paas.s3.cphc.get",
"component": "Запросы GET, HEAD, OPTIONS",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 0.95999999999999996,
"discount": 0,
"atomicCode": "paas.s3.cphc.get"
},
{
"paramId": 70,
"componentCode": "paas.s3.cphc.put",
"component": "Запросы PUT, POST, PATCH",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 11.640000000000001,
"discount": 0,
"atomicCode": "paas.s3.cphc.put"
}
],
"staticId": "20240911T125603868",
"serviceId": "a564eda1-b716-42ca-9ae7-efc49b112a58",
"serviceHash": "20240911T1256038683",
"LineCRMID": "019b6ec6-a949-7902-b473-8741f2a18efa",
"serviceCode": "paas.s3.cphc",
"endCustomerName": "",
"endCustomerINN": "",
"catalogName": "Объектное хранилище",
"discount": 0,
"quantity": 1,
"dateStartProvision": "20250826",
"dateEndProvision": "20250930",
"serviceStart": {
"currentTimeZoneName": "UTC",
"ts": "2025-08-26T00:00:00.000Z"
},
"serviceEnd": {
"currentTimeZoneName": "UTC",
"ts": "2025-09-30T00:00:00.000Z"
},
"serviceCustomName": "Объектное хранилище: S3 CEPH Холодное, в составе:\n Дисковое пространство: Объем хранения - 1 ГБайт\nИсходящий трафик - 1 ГБайт\nЗапросы GET, HEAD, OPTIONS - 1 10тыс.шт.\nЗапросы PUT, POST, PATCH - 1 10тыс.шт.\n",
"serviceType": 3,
"service_is_easy": false,
"vat": 20,
"vatRate": 0.20000000000000001,
"vatPerc": 20,
"vatFree": 0,
"priceWVat": "",
"serviceVersionGuid": "a564eda1-b716-42ca-9ae7-efc49b112a58",
"dealIndex": 3768,
"CalculationPeriod": "20260701"
},
{
"components": [
{
"paramId": 118,
"componentCode": "paas.s3.ceph.vol",
"component": "Дисковое пространство: Объем хранения",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.8,
"discount": 0,
"atomicCode": "paas.s3.ceph.vol"
},
{
"paramId": 69,
"componentCode": "paas.s3.ceph.trf",
"component": "Исходящий трафик",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.5,
"discount": 0,
"atomicCode": "paas.s3.ceph.trf"
},
{
"paramId": 68,
"componentCode": "paas.s3.ceph.get",
"component": "Запросы GET, HEAD, OPTIONS",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 0.34999999999999998,
"discount": 0,
"atomicCode": "paas.s3.ceph.get"
},
{
"paramId": 70,
"componentCode": "paas.s3.ceph.put",
"component": "Запросы PUT, POST, PATCH",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 4.5,
"discount": 0,
"atomicCode": "paas.s3.ceph.put"
}
],
"staticId": "20241015T124353835",
"serviceId": "5371b7cb-6f6c-4c7d-85b4-609eb52f994a",
"serviceHash": "20241015T1243538353",
"LineCRMID": "019b6ec6-aa03-7902-9c40-06f6d5cf6e13",
"serviceCode": "paas.s3.ceph",
"endCustomerName": "",
"endCustomerINN": "",
"catalogName": "Объектное хранилище",
"discount": 0,
"quantity": 1,
"dateStartProvision": "20250801",
"dateEndProvision": "20250930",
"serviceStart": {
"currentTimeZoneName": "UTC",
"ts": "2025-08-01T00:00:00.000Z"
},
"serviceEnd": {
"currentTimeZoneName": "UTC",
"ts": "2025-09-30T00:00:00.000Z"
},
"serviceCustomName": "Объектное хранилище: S3 CEPH, в составе:\n Дисковое пространство: Объем хранения - 1 ГБайт\nИсходящий трафик - 1 ГБайт\nЗапросы GET, HEAD, OPTIONS - 1 10тыс.шт.\nЗапросы PUT, POST, PATCH - 1 10тыс.шт.\n",
"serviceType": 3,
"service_is_easy": false,
"vat": 20,
"vatRate": 0.20000000000000001,
"vatPerc": 20,
"vatFree": 0,
"priceWVat": "",
"serviceVersionGuid": "5371b7cb-6f6c-4c7d-85b4-609eb52f994a",
"dealIndex": 3768,
"CalculationPeriod": "20260701"
},
{
"components": [
{
"paramId": 69,
"componentCode": "paas.s3.ceph.trf",
"component": "Исходящий трафик",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.5,
"discount": 0,
"atomicCode": "paas.s3.ceph.trf"
},
{
"paramId": 70,
"componentCode": "paas.s3.ceph.put",
"component": "Запросы PUT, POST, PATCH, LIST",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 4.5,
"discount": 0,
"atomicCode": "paas.s3.ceph.put"
},
{
"paramId": 118,
"componentCode": "paas.s3.ceph.vol",
"component": "Дисковое пространство: Объем хранения",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.8,
"discount": 0,
"atomicCode": "paas.s3.ceph.vol"
},
{
"paramId": 68,
"componentCode": "paas.s3.ceph.get",
"component": "Запросы GET, HEAD, OPTIONS",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 0.34999999999999998,
"discount": 0,
"atomicCode": "paas.s3.ceph.get"
}
],
"staticId": "e57bb0bd-8946-4603-95e8-c9c331378bce",
"serviceId": "09fd617a-9ffc-4b98-ad66-3fedac52c7b9",
"serviceHash": "e57bb0bd-8946-4603-95e8-c9c331378bce3",
"LineCRMID": "019a732b-22a2-7545-91ce-819207c779a0",
"catalogName": "Объектное хранилище",
"endCustomerName": "",
"endCustomerINN": "",
"serviceCode": "paas.s3.ceph",
"discount": 0,
"quantity": 1,
"dateStartProvision": "20251001",
"CalculationPeriod": "20260701",
"dateEndProvision": "20251231",
"serviceStart": {
"currentTimeZoneName": "UTC",
"ts": "2025-10-01T00:00:00.000Z"
},
"serviceEnd": {
"currentTimeZoneName": "UTC",
"ts": "2025-12-31T00:00:00.000Z"
},
"serviceCustomName": "Объектное хранилище: S3 CEPH, в составе:\n Исходящий трафик - 1 ГБайт\nЗапросы PUT, POST, PATCH, LIST - 1 10тыс.шт.\nДисковое пространство: Объем хранения - 1 ГБайт\nЗапросы GET, HEAD, OPTIONS - 1 10тыс.шт.\n",
"serviceType": 3,
"service_is_easy": false,
"vat": 20,
"vatRate": 0.20000000000000001,
"vatPerc": 20,
"vatFree": 0,
"priceWVat": "",
"serviceVersionGuid": "09fd617a-9ffc-4b98-ad66-3fedac52c7b9",
"dealIndex": 3274
},
{
"components": [
{
"paramId": 118,
"componentCode": "paas.s3.cphc.vol",
"component": "Дисковое пространство: Объем хранения",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.01,
"discount": 0,
"atomicCode": "paas.s3.cphc.vol"
},
{
"paramId": 69,
"componentCode": "paas.s3.cphc.trf",
"component": "Исходящий трафик",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.5,
"discount": 0,
"atomicCode": "paas.s3.cphc.trf"
},
{
"paramId": 68,
"componentCode": "paas.s3.cphc.get",
"component": "Запросы GET, HEAD, OPTIONS",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 0.95999999999999996,
"discount": 0,
"atomicCode": "paas.s3.cphc.get"
},
{
"paramId": 70,
"componentCode": "paas.s3.cphc.put",
"component": "Запросы PUT, POST, PATCH, LIST",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 11.640000000000001,
"discount": 0,
"atomicCode": "paas.s3.cphc.put"
}
],
"staticId": "2031f3ac-e921-4816-b91f-9b5b1cf5bf94",
"serviceId": "539d210f-2e5a-45e2-b148-b0b0834c138c",
"serviceHash": "2031f3ac-e921-4816-b91f-9b5b1cf5bf943",
"LineCRMID": "019a732b-22d4-7545-83af-18b7f8867eaa",
"catalogName": "Объектное хранилище",
"endCustomerName": "",
"endCustomerINN": "",
"serviceCode": "paas.s3.cphc",
"discount": 0,
"quantity": 1,
"dateStartProvision": "20251001",
"CalculationPeriod": "20260701",
"dateEndProvision": "20251231",
"serviceStart": {
"currentTimeZoneName": "UTC",
"ts": "2025-10-01T00:00:00.000Z"
},
"serviceEnd": {
"currentTimeZoneName": "UTC",
"ts": "2025-12-31T00:00:00.000Z"
},
"serviceCustomName": "Объектное хранилище: S3 CEPH Холодное, в составе:\n Дисковое пространство: Объем хранения - 1 ГБайт\nИсходящий трафик - 1 ГБайт\nЗапросы GET, HEAD, OPTIONS - 1 10тыс.шт.\nЗапросы PUT, POST, PATCH, LIST - 1 10тыс.шт.\n",
"serviceType": 3,
"service_is_easy": false,
"vat": 20,
"vatRate": 0.20000000000000001,
"vatPerc": 20,
"vatFree": 0,
"priceWVat": "",
"serviceVersionGuid": "539d210f-2e5a-45e2-b148-b0b0834c138c",
"dealIndex": 3274
},
{
"components": [
{
"paramId": 118,
"componentCode": "paas.s3.cphc.vol",
"component": "Дисковое пространство: Объем хранения",
"measureShort": "ГБайт",
"quantity": 40000,
"priceWVat": 1.01,
"discount": 0,
"atomicCode": "paas.s3.cphc.vol"
},
{
"paramId": 68,
"componentCode": "paas.s3.cphc.get",
"component": "Запросы GET, HEAD, OPTIONS",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 0.95999999999999996,
"discount": 0,
"atomicCode": "paas.s3.cphc.get"
},
{
"paramId": 69,
"componentCode": "paas.s3.cphc.trf",
"component": "Исходящий трафик",
"measureShort": "ГБайт",
"quantity": 1,
"priceWVat": 1.5,
"discount": 0,
"atomicCode": "paas.s3.cphc.trf"
},
{
"paramId": 70,
"componentCode": "paas.s3.cphc.put",
"component": "Запросы PUT, POST, PATCH",
"measureShort": "10тыс.шт.",
"quantity": 1,
"priceWVat": 11.640000000000001,
"discount": 0,
"atomicCode": "paas.s3.cphc.put"
}
],
"staticId": "20240911T125603868",
"serviceId": "67dffa20-a69e-449a-b02c-edf78d9ff192",
"serviceHash": "20240911T1256038683",
"LineCRMID": "01983726-3501-7496-b7f3-6f58f4d7f209",
"catalogName": "Объектное хранилище",
"endCustomerName": "",
"endCustomerINN": "",
"serviceCode": "paas.s3.cphc",
"discount": 0,
"quantity": 1,
"dateStartProvision": "20250801",
"CalculationPeriod": "20260701",
"dateEndProvision": "20250825",
"serviceStart": {
"currentTimeZoneName": "UTC",
"ts": "2025-08-01T00:00:00.000Z"
},
"serviceEnd": {
"currentTimeZoneName": "UTC",
"ts": "2025-08-25T00:00:00.000Z"
},
"serviceCustomName": "Объектное хранилище: S3 CEPH Холодное, в составе:\n Дисковое пространство: Объем хранения - 40000 ГБайт\nЗапросы GET, HEAD, OPTIONS - 1 10тыс.шт.\nИсходящий трафик - 1 ГБайт\nЗапросы PUT, POST, PATCH - 1 10тыс.шт.\n",
"serviceType": 3,
"service_is_easy": false,
"vat": 20,
"vatRate": 0.20000000000000001,
"vatPerc": 20,
"vatFree": 0,
"priceWVat": "",
"serviceVersionGuid": "67dffa20-a69e-449a-b02c-edf78d9ff192",
"dealIndex": 1450
}
]
}
]
}
]
}
-10
View File
@@ -1,10 +0,0 @@
REM set Windows env
setx rpt_class org.postgresql.Driver /M
setx rpt_connectionString jdbc:postgresql://write.09d78921-77a7-401a-b2cb-5ca11be850a0.services.ngcloud.ru:5432/rpt /M
setx rpt_username appserver /M
setx rpt_password encrypted:8b11c6e220c6456af3e08294e86cf91283d4119b58e25891e24719b0d27cbd4991d023c88da45113555c98d2cb5e4e969a4f585a94051527 /M
setx rpt_connectionLimit 5 /M
setx IDP_CLIENT_SECRET mXuJehst7ZZSBIFpjpLZFicYRhpLmxVs /M
pause
-10
View File
@@ -1,10 +0,0 @@
REM set Windows env
setx rpt_class org.postgresql.Driver /M
setx rpt_connectionString jdbc:postgresql://write.09d78921-77a7-401a-b2cb-5ca11be850a0.services.ngcloud.ru:5432/rpt /M
setx rpt_username smishchuk /M
setx rpt_password encrypted:8b11c6e220c6456af3e08294e86cf91283d4119b58e25891e24719b0d27cbd4991d023c88da45113555c98d2cb5e4e969a4f585a94051527 /M
setx rpt_connectionLimit 5 /M
setx IDP_CLIENT_SECRET mXuJehst7ZZSBIFpjpLZFicYRhpLmxVs /M
pause
-10
View File
@@ -1,10 +0,0 @@
REM set Windows env
setx rpt_class org.postgresql.Driver /M
setx rpt_connectionString jdbc:postgresql://write.09d78921-77a7-401a-b2cb-5ca11be850a0.services.ngcloud.ru:5432/rpt /M
setx rpt_username smishchuk /M
setx rpt_password encrypted:******** /M
setx rpt_connectionLimit 5 /M
setx IDP_CLIENT_SECRET ******** /M
pause
+159
View File
@@ -0,0 +1,159 @@
<cfsilent>
<cfimport prefix="m" taglib="lib"/>
<cfimport prefix="c" taglib="lib/controls"/>
<cfimport prefix="d" taglib="lib/data"/>
<cfimport prefix="layout" taglib="layout"/>
</cfsilent>
<m:prepare_ls entity="payg" accessObject="" pageInfoOut="pageInfo" trackOut="tr"/>
<cfparam name="dt_finish" type="date" default=#createDateTime(year(Now()),month(Now()),1,0,0,0)#/>
<cfparam name="dt_start" type="date" default=#dateAdd('m',-1,dt_finish)#/>
<cfset hours=dateDiff('h',dt_start,dt_finish)/>
<cfset tenant_wz_index=val(mid(request.auth.wz,3,5))/><!--- cut off WZ --->
<cfset tenant_wz_index="00169"/><!--- cut off WZ --->
<cfif isDefined("output_xls")><cfmodule template="mod/payg.cfm"
dt_finish=#dt_finish#
dt_start=#dt_start#
tenant_wz_index=#tenant_wz_index#
output="report"
debug=#isDefined("DEBUG")#
/><layout:xml qRead=#report.qCharge# titleMap=#report.chargeTitleMap# filename="#pageInfo.entity#.xml"/>
<cfabort/>
</cfif><cfif isDefined("output_json")><cfmodule template="mod/payg.cfm"
dt_finish=#dt_finish#
dt_start=#dt_start#
tenant_wz_index=#tenant_wz_index#
output="report"
debug=#isDefined("DEBUG")#
/><!--- <cfdump var=#report#/><cfabort/> ---><layout:json qRead=#report.qCharge# titleMap=#report.chargeTitleMap# filename="#pageInfo.entity#.json"/>
<cfabort/>
</cfif><!---
---><layout:page section="header" pageInfo=#pageInfo#>
<layout:attribute name="title">
<cfoutput><b>Отчет по PAYG</b> #request.auth.wz#</cfoutput>
</layout:attribute>
<layout:attribute name="controls">
<!---skip filter link, filter is not implemented--->
<!---<layout:language_switch/>--->
</layout:attribute>
</layout:page>
<!--- Внимание! округление вверх при расчете метрик get, put S3. Расчетные суммы могут содержать погрешности округления, поэтому окончательный расчет стоимости (количество * цену) должен выполняться в бухгалтерской программе.
<b>Если у клиента больше 1 действующего допника с услугами PAYG, то данные в отчете могут быть замножены, в этом случае считать вручную</b> --->
<cfoutput>
<form method="post" action="">
Период с <input type="text" name="dt_start" value="#dateFormat(dt_start,'YYYY-MM-DD')#"/>
по <input type="text" name="dt_finish" value="#dateFormat(dt_finish,'YYYY-MM-DD')#"/>
<input type="submit" style="cursor:pointer;"/>
<!--- <input type="submit" name="DEBUG" value="DEBUG" style="cursor:pointer;"/> --->
</form>
<cfflush/>
<cfmodule template="mod/payg.cfm"
dt_finish=#dt_finish#
dt_start=#dt_start#
tenant_wz_index=#tenant_wz_index#
output="report"
debug=#isDefined("DEBUG")#
/>
Часов в периоде: <b>#hours#</b> Строк в отчете: <b>#report.qCharge.recordCount#</b><br/>
Актуальность данных:
VCD Computing: <b>#dateFormat(report.qComputingAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qComputingAge.dt_load,'HH:MM:SS')#</b>
VCD Storage: <b>#dateFormat(report.qStorageAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qStorageAge.dt_load,'HH:MM:SS')#</b>
S3 хранение: <b>#dateFormat(report.qS3VolAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3VolAge.dt_load,'HH:MM:SS')#</b>
S3 операции и трафик: <b>#dateFormat(report.qS3OpsTrfAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3OpsTrfAge.dt_load,'HH:MM:SS')#</b>
<br/>
<a href="#request.thisPage#?output_xls" title="экспорт в Excel" style="margin-left:.5em; height:100%;" target="_blank"><img src="img/xls.gif" style="vertical-align:text-bottom;"/></a>
<a href="#request.thisPage#?output_json" title="экспорт в json" style="margin-left:.5em; height:100%;" target="_blank"><img src="img/json.svg" style="vertical-align:text-bottom;" width="13" height="13"/></a>
</cfoutput>
<table class="worktable">
<thead>
<td width="1%"></td>
<!--- <th width="3%">WZ</th> --->
<th width="7%">Артикул</th>
<th width="7%">Кол-во (метрика)</th>
<th width="3%">Ед.изм.</th>
<th width="3%">Цена &#8381; со скид., с НДС</th>
<th width="3%">Стоимость &#8381; с НДС</th>
<td width="3%">&nbsp;&nbsp;&nbsp;</td>
<td width="10%">Услуга</td>
<td width="10%">Компонент</td>
<!--- <td width="10%">Клиентс. назв.</td> --->
<td width="3%">GPL с НДС</td>
<td width="3%">Скидка%</td>
<!--- <td width="6%">Дата НОУ</td>
<td width="6%">Дата оконч.</td>
<td width="3%">Ключ строки</td> --->
<!--- <td width="3%">Сырая метрика</td> --->
<td width="3%">Оплачиваемый объем</td>
<!--- <td width="5%">Договор</td>
<td width="10%">Допник</td>
<td width="10%">Сделка</td> --->
</thead>
<cfoutput query="report.qCharge" group="svc">
<cfset var acc=0/>
<tr>
<td bgcolor="##ccc"></td>
<td bgcolor="##ccc" colspan="5" class="b"> #svc#</td>
<td></td>
<td bgcolor="##ccc" colspan="99"></td>
</tr>
<cfoutput>
<cfset acc=acc+charge/>
<tr>
<td>#currentRow#</a></td>
<td>#code#</td>
<td class="r" style="font-size:120%; padding:0 1em;">#chargeable_metric#</td>
<td class="c" style="font-size:90%;">#unit#</td>
<td class="r">#discounted_price#</td>
<td class="r">#charge#</td>
<td></td>
<td>#svc#</td>
<td>#component#</td>
<!--- <td>#user_description#</td> --->
<td class="r">#price#</td>
<td class="r">#discount#</td>
<td class="r">#metric#</td>
</tr>
</cfoutput>
<tr>
<td bgcolor="##eee" style="border-bottom:2px solid gray;"></td>
<td bgcolor="##eee" colspan="4" class="r" style="border-bottom:2px solid gray;"><b>Итого</b> #svc#:</td>
<td bgcolor="##eee" colspan="1" class="r b" style="border-bottom:2px solid gray;">#NumFmt(acc,2)#</td>
<td colspan="14" style="border-bottom:2px solid gray;"></td>
</tr >
</cfoutput>
</table>
<!--- <cfdump var=#qCharge#/> --->
<layout:page section="footer"/>
<!--- select * from s3billing.billing_per_user_fix_pl('2025-05-01 00:00:00','2025-06-01 00:00:00','1395'); --->
+12 -130
View File
@@ -4,30 +4,8 @@
<cfimport prefix="d" taglib="lib/data"/> <cfimport prefix="d" taglib="lib/data"/>
<cfimport prefix="layout" taglib="layout"/> <cfimport prefix="layout" taglib="layout"/>
</cfsilent> </cfsilent>
<m:prepare_ls entity="payg" accessObject="" pageInfoOut="pageInfo" trackOut="tr"/> <m:prepare_ls entity="payg" accessObject="" pageInfoOut="pageInfo" trackOut="tr"/>
<!---
<cfparam name="dt_finish" type="date" default=#createDateTime(year(Now()),month(Now()),1,0,0,0)#/>
<cfparam name="dt_start" type="date" default=#dateAdd('m',-1,dt_finish)#/>
<cfset hours=dateDiff('h',dt_start,dt_finish)/>
<cfset tenant_wz_index=val(mid(request.auth.wz,3,5))/><!--- cut off WZ --->
<cfif isDefined("output_xls")><cfmodule template="mod/payg.cfm"
dt_finish=#dt_finish#
dt_start=#dt_start#
output="report"
debug=#isDefined("DEBUG")#
/><layout:xml qRead=#report.qCharge# titleMap=#report.chargeTitleMap# filename="#pageInfo.entity#.xml"/>
<cfabort/>
</cfif><cfif isDefined("output_json")><cfmodule template="mod/payg.cfm"
dt_finish=#dt_finish#
dt_start=#dt_start#
output="report"
debug=#isDefined("DEBUG")#
/><!--- <cfdump var=#report#/><cfabort/> ---><layout:json qRead=#report.qCharge# titleMap=#report.chargeTitleMap# filename="#pageInfo.entity#.json"/>
<cfabort/>
</cfif><!---
---><layout:page section="header" pageInfo=#pageInfo#> ---><layout:page section="header" pageInfo=#pageInfo#>
<layout:attribute name="title"> <layout:attribute name="title">
@@ -39,117 +17,21 @@
</layout:attribute> </layout:attribute>
</layout:page> </layout:page>
<!--- Внимание! округление вверх при расчете метрик get, put S3. Расчетные суммы могут содержать погрешности округления, поэтому окончательный расчет стоимости (количество * цену) должен выполняться в бухгалтерской программе. <!--- <script type="text/javascript"/>
<b>Если у клиента больше 1 действующего допника с услугами PAYG, то данные в отчете могут быть замножены, в этом случае считать вручную</b> ---> $(document).ready(function() {
document.location="payg.cfm";
});
</script> --->
<cfoutput> <h1>
<form method="post" action=""> Подождите пожалуйста, формирование отчета может занять некоторое время
Период с <input type="text" name="dt_start" value="#dateFormat(dt_start,'YYYY-MM-DD')#"/> </h1>
по <input type="text" name="dt_finish" value="#dateFormat(dt_finish,'YYYY-MM-DD')#"/> <a href="payg.cfm">Если автоматического перенаправления не произошло, перейдите по ссылке</a>
<input type="submit" style="cursor:pointer;"/>
<!--- <input type="submit" name="DEBUG" value="DEBUG" style="cursor:pointer;"/> --->
</form>
<cfflush/>
<cfmodule template="mod/payg.cfm"
dt_finish=#dt_finish#
dt_start=#dt_start#
output="report"
debug=#isDefined("DEBUG")#
/>
Часов в периоде: <b>#hours#</b> Строк в отчете: <b>#report.qCharge.recordCount#</b><br/>
Актуальность данных:
VCD Computing: <b>#dateFormat(report.qComputingAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qComputingAge.dt_load,'HH:MM:SS')#</b>
VCD Storage: <b>#dateFormat(report.qStorageAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qStorageAge.dt_load,'HH:MM:SS')#</b>
S3 хранение: <b>#dateFormat(report.qS3VolAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3VolAge.dt_load,'HH:MM:SS')#</b>
S3 операции и трафик: <b>#dateFormat(report.qS3OpsTrfAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3OpsTrfAge.dt_load,'HH:MM:SS')#</b>
<br/>
<a href="#request.thisPage#?output_xls" title="экспорт в Excel" style="margin-left:.5em; height:100%;" target="_blank"><img src="img/xls.gif" style="vertical-align:text-bottom;"/></a>
<a href="#request.thisPage#?output_json" title="экспорт в json" style="margin-left:.5em; height:100%;" target="_blank"><img src="img/json.svg" style="vertical-align:text-bottom;" width="13" height="13"/></a>
</cfoutput>
<table class="worktable">
<thead>
<td width="1%"></td>
<!--- <th width="3%">WZ</th> --->
<th width="7%">Артикул</th>
<th width="7%">Кол-во (метрика)</th>
<th width="3%">Ед.изм.</th>
<th width="3%">Цена &#8381; со скид., с НДС</th>
<th width="3%">Стоимость &#8381; с НДС</th>
<td width="3%">&nbsp;&nbsp;&nbsp;</td>
<td width="10%">Услуга</td>
<td width="10%">Компонент</td>
<!--- <td width="10%">Клиентс. назв.</td> --->
<td width="3%">GPL с НДС</td>
<td width="3%">Скидка%</td>
<!--- <td width="6%">Дата НОУ</td>
<td width="6%">Дата оконч.</td>
<td width="3%">Ключ строки</td> --->
<!--- <td width="3%">Сырая метрика</td> --->
<td width="3%">Оплачиваемый объем</td>
<!--- <td width="5%">Договор</td>
<td width="10%">Допник</td>
<td width="10%">Сделка</td> --->
</thead>
<cfoutput query="report.qCharge" group="svc">
<cfset var acc=0/>
<tr>
<td bgcolor="##ccc"></td>
<td bgcolor="##ccc" colspan="5" class="b"> #svc#</td>
<td></td>
<td bgcolor="##ccc" colspan="99"></td>
</tr>
<cfoutput>
<cfset acc=acc+charge/>
<tr>
<td>#currentRow#</a></td>
<td>#code#</td>
<td class="r" style="font-size:120%; padding:0 1em;">#chargeable_metric#</td>
<td class="c" style="font-size:90%;">#unit#</td>
<td class="r">#discounted_price#</td>
<td class="r">#charge#</td>
<td></td>
<td>#svc#</td>
<td>#component#</td>
<!--- <td>#user_description#</td> --->
<td class="r">#price#</td>
<td class="r">#discount#</td>
<td class="r">#metric#</td>
</tr>
</cfoutput>
<tr>
<td bgcolor="##eee" style="border-bottom:2px solid gray;"></td>
<td bgcolor="##eee" colspan="4" class="r" style="border-bottom:2px solid gray;"><b>Итого</b> #svc#:</td>
<td bgcolor="##eee" colspan="1" class="r b" style="border-bottom:2px solid gray;">#NumFmt(acc,2)#</td>
<td colspan="14" style="border-bottom:2px solid gray;"></td>
</tr >
</cfoutput>
</table>
<!--- <cfdump var=#qCharge#/> --->
<layout:page section="footer"/> <layout:page section="footer"/>
<cflocation url="payg.cfm" addToken="No"/>
<!--- select * from s3billing.billing_per_user_fix_pl('2025-05-01 00:00:00','2025-06-01 00:00:00','1395'); ---> <!--- select * from s3billing.billing_per_user_fix_pl('2025-05-01 00:00:00','2025-06-01 00:00:00','1395'); --->
+124 -60
View File
@@ -1,78 +1,142 @@
<cfset msStartAt=getTickCount()/> <cfset msStartAt=getTickCount()/>
<cfparam name="dt_finish" type="date" default=#createDateTime(year(Now()),month(Now()),1,0,0,0)#/> <cfparam name="dt_finish" type="date" default=#dateAdd('d',-1,createDateTime(year(Now()),month(Now()),1,0,0,0))#/>
<cfparam name="dt_start" type="date" default=#dateAdd('m',-1,dt_finish)#/> <cfparam name="dt_start" type="date" default=#dateAdd('m',-1,createDateTime(year(Now()),month(Now()),1,0,0,0))#/>
<cfset hours=dateDiff('h',dt_start,dt_finish)/> <cfset hours=dateDiff('h', dt_start, dateAdd('d', 1, dt_finish))/>
<cfset tenant_wz_index=val(mid(request.auth.wz,3,5))/><!--- cut off WZ ---> <cfset tenant_wz_index=val(mid(request.auth.wz,3,5))/><!--- cut off WZ --->
<cffunction name="snake2camel"
<cfscript>
function gaussianRound(numeric number, numeric precision = 0) {
var factor = 10 ^ precision;
var scaledNumber = number * factor;
var absScaled = abs(scaledNumber);
var sign = sgn(scaledNumber);
var floored = int(absScaled);
// If it's not ending in exactly .5, use standard rounding or precision scaling
if (absScaled - floored != 0.5) {
return round(number * factor) / factor;
}
// If it is exactly .5, round towards the nearest even number
if (floored % 2 != 0) {
// Nearest even is UP
return (floored + 1) * sign / factor;
} else {
// Nearest even is DOWN
return floored * sign / factor;
}
}
</cfscript>
<cffunction name="iso8601dtFormat"
returntype="string" returntype="string"
output="false" output="false"
hint="convert snake style name to camel style name"> hint="formates date and time like this: 2023-12-18T15:41:42.516+0300">
<cfargument name="snake" type="string" required="true" /> <cfargument name="input" required="true" />
<cfreturn #reReplace(ARGUMENTS.snake,"_([a-z])","\u\1","ALL")#/> <cfreturn "#dateFormat( input, "yyyy-mm-dd" )#T#timeFormat( input, "HH:mm:ss.lXX" )#"/>
</cffunction>
<cffunction name="camel2snake"
returntype="string"
output="false"
hint="convert camel style name to snake style name">
<cfargument name="camel" type="string" required="true" />
<cfreturn #reReplace(ARGUMENTS.camel,"([A-Z])","_\l\1","ALL")#/>
</cffunction>
<cffunction name="query2json"
output="false"
hint="print query in json format as array of structures">
<cfargument name="qry" type="query" required="true"/><!--- column names should not contain commas --->
<cfargument name="convertSnakeToCamel" type="boolean" default=true/>
<cfset var columnsIn=#arguments.qry.columnList()#/>
<cfset var columnsOut=""/>
<cfif arguments.convertSnakeToCamel>
<cfloop list=#columnsIn# item="col">
<cfset columnsOut=listAppend(columnsOut,snake2camel(col))/>
</cfloop>
<cfset var outArray=arrayNew(1)/>
//copy fields to structure, renaming fields, this preserves data type
<cfloop query=#arguments.qry#>
<cfset var structRow=structNew()/>
<cfloop index="i" from="1" to=#listLen(columnsOut)#>
<cfset structInsert(structRow,"#listGetAt(#columnsOut#,i)#",arguments.qry["#listGetAt(#columnsIn#,i)#"])/>
</cfloop>
<cfset arrayAppend(outArray,structRow)/>
</cfloop>
<cfreturn serializeJSON(outArray)/>
<cfelse>
<cfreturn serializeJSON(arguments.qry,"struct")/>
</cfif>
</cffunction> </cffunction>
<cfmodule template="mod/payg.cfm" <cfmodule template="mod/compute_payg.cfm"
dt_finish=#dt_finish# dt_finish=#dt_finish#
dt_start=#dt_start# dt_start=#dt_start#
tenant_wz_index=#tenant_wz_index#
output="report" output="report"
debug=#isDefined("DEBUG")# debug=#isDefined("DEBUG")#
/> />
<cfset out={ <!--- "dtStart"="#dateFormat(dt_start, "yyyy-mm-dd")#"
"hours"=#hours#, ,"dtFinish"="#dateFormat(dt_finish, "yyyy-mm-dd")#" --->
"vcdComputingLoadedAt"="#dateFormat(report.qComputingAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qComputingAge.dt_load,'HH:MM:SS')#", <cfset temp.out=[
"vcdStorageLoadedAt"="#dateFormat(report.qStorageAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qStorageAge.dt_load,'HH:MM:SS')#", "dtStart="=#iso8601dtFormat(dt_start)#
"s3StorageLoadedAt"="#dateFormat(report.qComputingAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3VolAge.dt_load,'HH:MM:SS')#", ,"dtFinish"=#iso8601dtFormat(dt_finish)#
"s3OpsTrfLoadedAt"="#dateFormat(report.qS3OpsTrfAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3OpsTrfAge.dt_load,'HH:MM:SS')#", ,"hours"=#hours#
"records"=#report.qCharge.recordCount#, ,"tenantWzIndex"="#tenant_wz_index#"
"queryDurationMs"=#(getTickCount() - msStartAt)#, ,"wz"=#request.auth.wz#
"report"=#report.qCharge.reduce((result, row) => { ,"login"=#request.auth.login#
result.append(row); ,"appVersion"=#request.APP_VERSION#
return result; ,"vcdComputingLoadedAt"="#iso8601dtFormat(report.qComputingAge.dt_load)#"
}, [])# ,"vcdStorageLoadedAt"="#iso8601dtFormat(report.qStorageAge.dt_load)#"
}/> ,"s3StorageLoadedAt"="#iso8601dtFormat(report.qComputingAge.dt_load)#"
,"s3OpsTrfLoadedAt"="#iso8601dtFormat(report.qS3OpsTrfAge.dt_load)#"
,"records"=#report.qCharge.recordCount#
,"queryDurationMs"=#(getTickCount() - msStartAt)#
,"periods"=[]
]/><!--- using [] instead of {} for struct literals to obtain linked (ordered) structures: keys order preserved --->
<!--- beware nulls --->
<cfoutput query="report.qCharge" group="year">
<cfoutput group="month">
<cfset temp.period=[
"year"=#year#
,"month"=#month#
,"services"=[]
]/>
<cfset arrayAppend(temp.out.periods,temp.period)/>
<cfoutput group="svc">
<cfset var accum_cost_svc = 0/>
<!--- <cfset var accum_metric_component = 0/> --->
<cfset "temp.service"=["svc"="#svc#"
,"serviceCode"="#service_code#"
,"serviceCustomName"="#serviceCustomName#"
<!--- ,"note"="#(serviceHash EQ '')?'не найдено в договоре':''#" --->
,"hash"="#serviceHash#"
<!--- ,"metric"=#accum_metric_component# --->
,"components"=[]
]/>
<cfoutput group="code">
<cfoutput group="serviceStart">
<!--- <cfset accum_metric_component += isNumeric(metric) ? metric : 0/> --->
<cfset accum_metric_dimension_1 = 0/>
<cfset "temp.component"=["code"="#code#"
,"codeMetered"="#m_code#"
,"component"="#component#"
,"unit"="#unit#"
,"freeTier"="#free_tier#"
,"price"="#gaussianRound(discounted_price,2)#"
,"details"=[]
]/>
<cfoutput group="dimension_1">
<cfset accum_metric_dimension_1 += isNumeric(metric) ? metric : 0/>
<cfset temp.dimension=["dimension1name"="#dimension_1_name#"
,"dimension1value"="#dimension_1#"
,"dtBilledFrom"="#dateFormat(dt_billed_from,'YYYY-MM-DD')#"
,"dtBilledTo"="#dateFormat(dt_billed_to,'YYYY-MM-DD')#"
,"metric"=#(metric NEQ 0 AND isNumeric(metric))?round(metric,3):javaCast("null",0)#
,"unit"="#unit#"
<!--- ,"price"="#gaussianRound(discounted_price,2)#" --->
]/>
<cfset arrayAppend(temp.component.details, temp.dimension)/>
</cfoutput><!--- dimension_1 --->
<!--- считаем, что free_tier NOT NULL (вместо NULL 0) --->
<cfset var payable_metric_dimension_1 = (isNumeric(accum_metric_dimension_1))? max(ceiling(accum_metric_dimension_1) - free_tier, 0):javaCast("null",0)/>
<cfset var cost_dimension_1 = precisionEvaluate("gaussianRound(payable_metric_dimension_1*gaussianRound(discounted_price, 2), 2)")/>
<cfset accum_cost_svc = precisionEvaluate("accum_cost_svc + cost_dimension_1")/>
<cfset "temp.component.metric"=#(accum_metric_dimension_1 NEQ 0 AND isNumeric(accum_metric_dimension_1))? round(accum_metric_dimension_1,3):javaCast("null",0)#/>
<cfset "temp.component.metricPayable"=#payable_metric_dimension_1#/>
<cfset "temp.component.cost"=#cost_dimension_1#/>
<cfset arrayAppend(temp.service.components, temp.component)/>
</cfoutput><!--- serviceStart (component version in specification, that is svc version joined down to the component) --->
<cfset "temp.service.cost"=#accum_cost_svc#/>
</cfoutput><!--- code (component) --->
<cfset arrayAppend(temp.period.services, temp.service)/>
</cfoutput><!--- svc --->
</cfoutput><!--- month --->
</cfoutput><!--- year --->
<cfset "temp.out.durationMs"=(getTickCount()-request.startTickCount)/>
<cfcontent <cfcontent
type="application/json" type="application/json"
/><cfoutput>#serializeJSON(out)#</cfoutput> /><cfoutput>#serializeJSON(temp.out)#</cfoutput>
+566
View File
@@ -0,0 +1,566 @@
<cfimport prefix="m" taglib="../lib"/>
<cfimport prefix="d" taglib="../lib/data"/>
<cfparam name="ATTRIBUTES.dt_finish" type="date" default=#createDateTime(year(Now()),month(Now()),1,0,0,0)#/>
<cfparam name="ATTRIBUTES.dt_start" type="date" default=#dateAdd('m',-1,ATTRIBUTES.dt_finish)#/>
<cfparam name="ATTRIBUTES.output" type="string" default="report"/>
<cfparam name="ATTRIBUTES.debug" type="boolean" default=false/>
<cfparam name="ATTRIBUTES.tenant_wz_index" type="string"/>
<cfparam name="ATTRIBUTES.restrict_to_own_instances" type="boolean" default=true/>
<cfset hours=dateDiff('h', ATTRIBUTES.dt_start, dateAdd("d",1,ATTRIBUTES.dt_finish))/> <!--- *** нужно для каждого фрагмента --->
<cfif hours LE 0>
<cfthrow message="Некорректный период" detail="Период должен быть больше 0"/>
</cfif>
<!---
*** ВНИМАНИЕ! Вычитать минимальный бесплатный лимит (free tier) нужно после агрегации!
Исходная последовательность расчетов сначала делает агрегацию по месяцу, при расчете по дням нужно менять порядок
--->
<!---
Скрытое противоречие, которое требует разрешения: если у нас несколько периодов тарификации в одном месяце (по разной цене), ИЗ КАКОГО вычитать минимальный лимит?
Еще сложность, если лимита хватает на несколько периодов тарификации, как его распределять по периодам. Если тарификация строго по месяцам, этой проблемы нет.
Это можно сделать процедурно, но сложность расчета все возрастает
--->
<!--- *** Проверить, что у нас не биллится закрытый период (закрытый следующим) - возможно, с учетом line_key
- насколько вижу, прошлый период закрывается, причем концы отрезки закрытые (конец включается). Считаем, что этой проблемы нет
--->
<!--- QoQ does not support join syntax, and of course outer joins, at least of Lucee 5.4--->
<!--- а внешние джойны хорошо подсвечивают ошибки в данных, особенно помогает full --->
<cfmodule template="crm_contract.cfm" wz="WZ#numberFormat(ATTRIBUTES.tenant_wz_index,'00000')#" out="qSpecification"/>
<cfif (ATTRIBUTES.debug)>
qSpecification (from CRM)
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qSpecification#/>
<cfflush/>
</cfif>
<cfquery name="qComputingAge">
select max(capacity_resource.timestamp) as dt_load from ngcloud_ru.capacity_resource
</cfquery>
<cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qComputingAge#/>
<cfflush/>
</cfif>
<cfquery name="qStorageAge" datasource="rpt">
select max(capacity_storage.timestamp) as dt_load from ngcloud_ru.capacity_storage
</cfquery>
<cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qStorageAge#/>
<cfflush/>
</cfif>
<cfquery name="qS3VolAge">
select max(timestamp_addition) as dt_load from s3billing.bucket_stat
</cfquery>
<cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qS3VolAge#/>
<cfflush/>
</cfif>
<cfquery name="qS3OpsTrfAge">
select max(time) as dt_load from s3billing.usage_bucket_by_user
</cfquery>
<cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qS3OpsTrfAge#/>
<cfflush/>
</cfif>
<cfset qFreeTier=queryNew("code,free_tier","CF_SQL_VARCHAR,CF_SQL_NUMERIC",[['paas.s3.ceph.vol-m',1],['paas.s3.ceph.get-m',10],['paas.s3.ceph.put-m',1],['paas.s3.ceph.trf-m',100]])/>
<!--- -- все-таки пришлось прибить гвоздями - в каталоге нет даже бесплатного лимита, не то что скидочной политики
-- придется добавить в каталог
-- бесплатный лимит есть только для S3 HOT (в месяц в единицах измерения) --->
<!--- ******************************** здесь нужно организовать пересечение версионной спецификации с анализируемым диапазоном дат --->
<!--- Проверка: включаем дату смены цены в диапазон и смотрим --->
<!--- Важно: если в периоде было 2+ цены, то строки должны быть замножены, а цена за период перестает иметь смысл (не показывать же среднее арифметическое, это неправильно) --->
<!--- Left Outer Join with Free Tier --->
<cfquery name="qSpec" dbtype="query">
select s.catalogName as svc, s.component, s.serviceCode as service_code, s.fullCode as code, s.measureShort as unit
, s.priceWVat as price, (s.discount + s.serviceDiscount - s.discount*s.serviceDiscount/100) as discount
,s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, f.free_tier
from qSpecification s, qFreeTier f
WHERE s.fullCode=f.code
AND s.serviceType = 3
UNION
select s.catalogName as svc, s.component, s.serviceCode as service_code, s.fullCode as code, s.measureShort as unit
, s.priceWVat as price, (s.discount + s.serviceDiscount - s.discount*s.serviceDiscount/100) as discount
,s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, 0
from qSpecification s
WHERE s.fullCode NOT IN (<cfqueryparam value="#ValueList(qFreeTier.code)#" cfsqltype="cf_sql_varchar" list="true">)
AND s.serviceType = 3
</cfquery>
<cfif (ATTRIBUTES.debug)>
qSpec (Left Outer Join with Free Tier)
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qSpec#/>
<cfflush/>
</cfif>
<!--- -- 1. Create a specialized expression index matching your date filters
CREATE INDEX idx_capacity_storage_perf_test ON ngcloud_ru.capacity_storage (
vdc_id,
(timestamp::date),
storage_profile_types_id
)
INCLUDE (limit, used);
-- 2. Optional: Standardize tenant scanning since it currently falls back to Seq Scan
CREATE INDEX idx_tenant_wzcode ON ngcloud_ru.tenant (wzcode); --->
<!--- *** Нужно еще реализовать в модели данных (каталоге) политики бесплатного минимума (free tier), сейчас прибито гвоздями --->
<cfquery name="qComputing">
select 'WZ'||to_char(tenant.wzcode,'FM00000') as wz, vdc.name as vdc_name,
case
WHEN vdc.name LIKE '%-v1cl1%' THEN 'iaas.ngc.i29'
WHEN vdc.name LIKE '%-v1cl2%' THEN 'iaas.ngc.i31'
WHEN vdc.name LIKE '%-v1cl4%' THEN 'iaas.ngc.a28'
WHEN vdc.name LIKE '%-v1cl5%' THEN 'iaas.ngc.a45'
WHEN vdc.name LIKE '%-v1cl6%' THEN 'iaas.ngc.i28'
WHEN vdc.name LIKE '%-v1cl7%' THEN 'iaas.ngc.a40' -- *** смапить все кластера
ELSE '['||vdc.name||']' end as code,
sum(cpu_used/cpu_speed)/4. as core_h,
sum(mem_used)/1024/4. as gb_h,
capacity_resource.timestamp::date as dt,
vdc.name as dimension_1,
null as dimension_2
from ngcloud_ru.capacity_resource
join ngcloud_ru.vdc on capacity_resource.vdc_id=vdc.id
join ngcloud_ru.tenant on tenant.id=vdc.tenant_id
where tenant.wzcode = <cfqueryparam cfsqltype="cf_sql_integer" value=#ATTRIBUTES.tenant_wz_index#/>
AND capacity_resource.timestamp >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#attributes.dt_start#/>
AND capacity_resource.timestamp < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dateAdd("d",1,attributes.dt_finish)#/>
group by tenant.wzcode, vdc.name, capacity_resource.timestamp::date
order by tenant.wzcode, vdc.name, capacity_resource.timestamp::date;
</cfquery>
<cfif (ATTRIBUTES.debug)>
qComputing
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qComputing#/>
<cfflush/>
</cfif>
<!--- если захочется сгенерить литерал таблицы соответствия
with a as (
select * from ngcloud_ru.storage_profile_types
)
select '('||id||', '''|| name||''', '''||
case
when name ilike '%fastssd%' then 'fstssd'
when name ilike '%ssd%' then 'ssd'
when name ilike '%sata%' then 'sata'
else '' end
||''''||')'
from a
--->
<!--- переделать storage_profile_types с расшифровкой ssd, sata, можно прямо до каталожного кода --->
<!--- очень долго исполняется --->
<cfquery name="qStorage">
select 'WZ'||to_char(tenant.wzcode,'FM00000') as wz, vdc.name as vdc_name,
case
WHEN vdc.name LIKE '%-v1cl1%' THEN 'iaas.ngc.i29'
WHEN vdc.name LIKE '%-v1cl2%' THEN 'iaas.ngc.i31'
WHEN vdc.name LIKE '%-v1cl4%' THEN 'iaas.ngc.a28'
WHEN vdc.name LIKE '%-v1cl5%' THEN 'iaas.ngc.a45'
WHEN vdc.name LIKE '%-v1cl6%' THEN 'iaas.ngc.i28'
WHEN vdc.name LIKE '%-v1cl7%' THEN 'iaas.ngc.a40'
ELSE '['||vdc.name||']' END
|| CASE
WHEN storage_profile_types.name ILIKE '%FASTSSD%' THEN '.fstssd-m'
WHEN storage_profile_types.name ILIKE '%SSD%' THEN '.ssd-m'
WHEN storage_profile_types.name ILIKE '%SAS%' THEN '.sas-m'
WHEN storage_profile_types.name ILIKE '%SATA%' THEN '.sata-m'
ELSE '.['|| storage_profile_types.name ||']' END
as code,
storage_profile_types.name, -- *** это не тип, а профайл
sum("limit")/1024./4./720 as gb_m_limit,
sum(used)/1024./4./720 as gb_m_used,
capacity_storage.timestamp::date as dt,
vdc.name as dimension_1,
storage_profile_types.name as dimension_2
from ngcloud_ru.capacity_storage
join ngcloud_ru.vdc on capacity_storage.vdc_id=vdc.id
join ngcloud_ru.tenant on tenant.id=vdc.tenant_id
join ngcloud_ru.storage_profile_types on capacity_storage.storage_profile_types_id=storage_profile_types.id
where tenant.wzcode = <cfqueryparam cfsqltype="cf_sql_integer" value=#ATTRIBUTES.tenant_wz_index#/> --AND 1=0
AND capacity_storage.timestamp >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#attributes.dt_start#/>
AND capacity_storage.timestamp < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dateAdd("d",1,attributes.dt_finish)#/>
group by tenant.wzcode, vdc.name, storage_profile_types.name, capacity_storage.timestamp::date
order by tenant.wzcode, vdc.name, storage_profile_types.name, capacity_storage.timestamp::date
;
</cfquery>
<!--- create index IX_capacity_storage_sort on ngcloud_ru.capacity_storage(vdc_id,storage_profile_types_id,timestamp); --->
<cfif (ATTRIBUTES.debug)>
qStorage
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qStorage#/>
<cfflush/>
</cfif>
<!---
"placement" "size_gb" "get_10k" "put_10k" "bytes_sent_gb"
HOT_FREE_LIMIT 1 10 1 100
--->
<!--- надо дисконтировать не ГБ-часы а ГБ-месяцы --->
<cfquery name="qS3Vol">
select /* *** ниже дублирование кода агрегации - round ceil etc, обратить внимание при правке */
/* *** переделать на avg - сначала агрегировать по бакетам*/
(sum(bucket_stat.usage_rgw_main_size_actual)/720) as vol_B
,ceil(sum(bucket_stat.usage_rgw_main_size_actual)/720/1024/1024/1024) as vol_GB
<!--- ,greatest(ceil(sum(bucket_stat.usage_rgw_main_size_actual)/<cfqueryparam cfsqltype="cf_sql_integer" value=#hours#/>
/1024/1024/1024)-
case when bucket_stat.placement_id = 1 then 1 else 0 end, 0) as vol_GB_free_tier_subtracted /*считается не в точности так, как в функции*/ --->
,case
when bucket_stat.placement_id = 1 then 'paas.s3.ceph'
when bucket_stat.placement_id = 2 then 'paas.s3.cphc'
else '['|| TO_CHAR(bucket_stat.placement_id,'FM9') ||']' end as code
--,bucket_stat.placement_id
,'WZ'||lpad(bucket_stat.owner, 5, '0') as wz -- *** здесь не число, а строка, поэтому другая формула обратной сборки WZ
,bucket_stat.timestamp_addition::date as dt
,bucket_info.name || ' - ' || case
when bucket_stat.placement_id = 1 then 'paas.s3.ceph'
when bucket_stat.placement_id = 2 then 'paas.s3.cphc'
else '['|| TO_CHAR(bucket_stat.placement_id,'FM9') ||']' end
as dimension_1
,null as dimension_2
from s3billing.bucket_stat
join s3billing.placement ON s3billing.placement.id=bucket_stat.placement_id
join s3billing.bucket_info ON bucket_info.id=bucket_stat.bucket_id
where bucket_stat.placement_id in (1,2)
AND bucket_stat.owner = <cfqueryparam cfsqltype="cf_sql_varchar" value=#ATTRIBUTES.tenant_wz_index#/>
AND bucket_stat.timestamp_addition >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#attributes.dt_start#/>
AND bucket_stat.timestamp_addition < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dateAdd("d",1,attributes.dt_finish)#/>
group by bucket_stat.owner, bucket_stat.placement_id, bucket_info.name, bucket_stat.timestamp_addition::date
order by bucket_stat.owner, bucket_stat.placement_id, bucket_info.name, bucket_stat.timestamp_addition::date;
</cfquery>
<cfif (ATTRIBUTES.debug)>
qS3Vol
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qS3Vol#/>
<cfflush/>
</cfif>
<cfquery name="qS3OpsTrf">
select
sum(
CASE WHEN
(category.name LIKE 'get%'
OR category.name LIKE 'head%'
OR category.name LIKE 'options%'
) THEN ops ELSE 0 END
) as get
,ceil(sum(
CASE WHEN
(category.name LIKE 'get%'
OR category.name LIKE 'head%'
OR category.name LIKE 'options%'
) THEN ops ELSE 0 END
)/10000) as get_10k
/*,greatest(ceil(sum(
CASE WHEN
(category.name LIKE 'get%'
OR category.name LIKE 'head%'
OR category.name LIKE 'options%'
) THEN ops ELSE 0 END
)/10000) - CASE WHEN bucket_info.placement_id = 1 THEN 10 ELSE 0 END, 0
) as get_10k_free_tier_subtracted*/
,sum(
CASE WHEN
(category.name LIKE 'put%'
OR category.name LIKE 'post%'
OR category.name LIKE 'patch%'
OR category.name LIKE 'list%'
) THEN ops ELSE 0 END
) as put
,ceil(sum(
CASE WHEN
(category.name LIKE 'put%'
OR category.name LIKE 'post%'
OR category.name LIKE 'patch%'
OR category.name LIKE 'list%'
) THEN ops ELSE 0 END
)/10000) as put_10k
/*,greatest(ceil(sum(
CASE WHEN
(category.name LIKE 'put%'
OR category.name LIKE 'post%'
OR category.name LIKE 'patch%'
OR category.name LIKE 'list%'
) THEN ops ELSE 0 END
)/10000) - CASE WHEN bucket_info.placement_id = 1 THEN 1 ELSE 0 END, 0
) as put_10k_free_tier_subtracted*/
,(sum(bytes_sent)) as bytes_sent
,ceil(sum(bytes_sent)/1024/1024/1024) as bytes_sent_GB
/*,greatest(ceil(sum(bytes_sent)/1024/1024/1024) - CASE WHEN bucket_info.placement_id = 1 THEN 100 ELSE 0 END, 0
) as bytes_sent_GB_free_tier_subtracted */
,case
when bucket_info.placement_id = 1 then 'paas.s3.ceph'
when bucket_info.placement_id = 2 then 'paas.s3.cphc'
else '['|| TO_CHAR(bucket_info.placement_id,'FM9') ||']' end as code --*** зачем делать там ключ bigint
,bucket_info.placement_id
--category.name, bucket_info.name
,'WZ'||lpad(user_info.name, 5, '0') as wz
,usage_bucket_by_user.time::date as dt
,bucket_info.name || ' - ' || case
when bucket_info.placement_id = 1 then 'paas.s3.ceph'
when bucket_info.placement_id = 2 then 'paas.s3.cphc'
else '['|| TO_CHAR(bucket_info.placement_id,'FM9') ||']' end
as dimension_1
,null as dimension_2
from s3billing.usage_bucket_by_user
join s3billing.user_info on usage_bucket_by_user.user_id=user_info.id
join s3billing.category on usage_bucket_by_user.category_id=category.id
join s3billing.bucket_info on usage_bucket_by_user.bucketid=bucket_info.id
join s3billing.placement on bucket_info.placement_id=placement.id
where 1=1
AND user_info.name = <cfqueryparam cfsqltype="cf_sql_varchar" value=#ATTRIBUTES.tenant_wz_index#/>
AND usage_bucket_by_user.time >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#attributes.dt_start#/>
AND usage_bucket_by_user.time < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#dateAdd("d",1,attributes.dt_finish)#/>
group by user_info.name, bucket_info.placement_id, bucket_info.name, usage_bucket_by_user.time::date
order by user_info.name, bucket_info.placement_id, bucket_info.name, usage_bucket_by_user.time::date;
</cfquery>
<!--- здесь метрики уже сгруппированы по дням. Умножать на цену еще рано, надо сильно позже --->
<cfif (ATTRIBUTES.debug)>
qS3OpsTrf
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qS3OpsTrf#/>
<cfflush/>
</cfif>
<!--- собрали все метрики в один резалтсет --->
<!--- Здесь мы знаем единицы измерения, причем надежнее, чем в спецификации --->
<cfquery name="qUnifiedMetric" dbType="query">
select dt, wz, code + '.vcpu-m' as code, core_h as raw_metric, core_h as metric,
dimension_1, 'vdc' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ядро-ч' as unit
from qComputing
union all
select dt, wz, code + '.ram-m' as code, gb_h as raw_metric, gb_h as metric,
dimension_1, 'vdc' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ГБайт-ч' as unit
from qComputing
union all
select dt, wz, code, gb_m_used as raw_metric, gb_m_used as metric,
dimension_1, 'vdc' as dimension_1_name, dimension_2, 'storage policy' as dimension_2_name, 'ГБайт-м' as unit
from qStorage
union all
select dt, wz, code + '.vol-m', vol_b as raw_metric, vol_gb as metric,
dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ГБайт-м' as unit
from qS3Vol
union all
select dt, wz, code + '.get-m', get as raw_metric, get_10k as metric,
dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, '10тыс.шт.' as unit
from qS3OpsTrf
union all
select dt, wz, code + '.put-m', put as raw_metric, put_10k as metric,
dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, '10тыс.шт.' as unit
from qS3OpsTrf
union all
select dt, wz, code + '.trf-m', bytes_sent as raw_metric, bytes_sent_gb as metric,
dimension_1, 'bucket' as dimension_1_name, dimension_2, NULL as dimension_2_name, 'ГБайт' as unit
from qS3OpsTrf
order by dt, code, dimension_1, dimension_2 <!--- /*dimension_*_name нужно только в качестве заголовка *** потом оптимизировать */ --->
</cfquery>
<!--- clean extra spaces to work around QoQ bug --->
<cfloop query="qUnifiedMetric">
<cfset qUnifiedMetric.dimension_1_name=trim(qUnifiedMetric.dimension_1_name)/>
<cfset qUnifiedMetric.dimension_2_name=trim(qUnifiedMetric.dimension_2_name)/>
<!--- *** возможно, и с другими полями придется сделать то же самое --->
</cfloop>
<!--- This is a known, long-standing quirk in ColdFusion's Query of Queries (QoQ) engine.When you perform a UNION ALL across queries where a character column has a dynamic or varying string length (like your concatenated m.dimension_1_name + 'x'), QoQ identifies the longest string in that column across all result sets. It then treats the column like a fixed-length CHAR database field and pads all shorter values with trailing spaces to match that maximum length. --->
<!--- лучше никогда не связываться с QoQ, но если все исполнять в исходной бд, это скорее мегаселект с большим числом CTE --->
<!--- надо было сразу делать в БД. Причем сводить в одну, чтобы избежать нескольких источников данных... можно было бы и так добиться, но все равно сводим в буферную базу --->
<!--- <cfdump var=#qUnifiedMetric#/> --->
<cfif (ATTRIBUTES.debug)>
qUnifiedMetric
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qUnifiedMetric#/>
<cfflush/>
</cfif>
<!--- умножаем на цену, вычисляем дисконт (довольно поздно) и форматируем
*** здесь нужно учесть версионность. Самым простым кажется декомпозировать по дням и снова собрать
*** только как нам это сделать без нормального SQL движка
Может быть, процедурная обработка не будет выглядеть ужасно, и не хуже SQL
В конце концов, можно просто В ЦИКЛЕ обсчитать диапазон по дням, выполнив запрос 30 раз, или сколько надо
Если будет тупить - оптимизируем
--->
<!--- Для декомпозиции на более мелкие объекты мы можем как полумеру - профильтровать по конкретному бакету или ВМ-ВДЦ
Что не нравится - для ВМ у нас может быть целое дерево нижележащих объектов вместо линейного списка бакетов --->
<!--- Вычитание Free Tier можно начинать с начала месяца - для этого можно сгруппировать по месяцам --->
<!--- Кажется, здесь еще рано агрегировать, если мы хотим OUTER JOIN --->
<!--- агрегировать вообще не надо в этом компоненте, а стоимость вычислять после агрегации --->
<cfquery name="qChargeDaily" dbType="query">
SELECT
<d:field_set titleMapOut="titleMap" lengthOut="fieldCount">
<d:field title="Код компонента">s.code as s_code</d:field>
<d:field title="Код метрики">m.code as m_code</d:field>
<d:field title="Код услуги">s.service_code as service_code</d:field>
<d:field title="Услуга">s.svc</d:field>
<d:field title="Компонент">s.component</d:field>
<d:field title="Ед.изм.">m.unit</d:field>
<d:field title="Цена &##8381; с НДС" cfSqlType="CF_SQL_NUMERIC">
s.price*(100-s.discount)/100 as discounted_price
</d:field>
<d:field title="Цена с НДС" cfSqlType="CF_SQL_NUMERIC">s.price</d:field>
<d:field title="Скидка%" cfSqlType="CF_SQL_NUMERIC">s.discount</d:field>
<d:field title="Сырая метрика" cfSqlType="CF_SQL_NUMERIC">m.raw_metric</d:field>
<d:field title="Приведенная метрика" cfSqlType="CF_SQL_NUMERIC">m.metric</d:field>
<d:field title="serviceStart" cfSqlType="CF_SQL_TIMESTAMP">s.serviceStart</d:field>
<d:field title="serviceEnd" cfSqlType="CF_SQL_TIMESTAMP">s.serviceEnd</d:field>
<d:field title="serviceHash">s.serviceHash</d:field>
<d:field title="serviceCustomName">s.serviceCustomName</d:field>
<d:field title="dt" cfSqlType="CF_SQL_TIMESTAMP">m.dt</d:field>
<d:field title="free tier" cfSqlType="CF_SQL_NUMERIC">s.free_tier</d:field>
<d:field>m.dimension_1</d:field>
<d:field>m.dimension_1_name</d:field>
<d:field>m.dimension_2</d:field>
<d:field>m.dimension_2_name</d:field>
</d:field_set>
FROM qSpec as s, qUnifiedMetric as m
WHERE s.code=m.code
AND s.serviceStart <= m.dt
AND (m.dt <= s.serviceEnd OR s.serviceEnd IS NULL)
<cfif #attributes.restrict_to_own_instances#>
AND m.wz=<cfqueryparam cfsqltype="cf_sql_varchar" value=#request.auth.wz#/>
</cfif>
<!--- таким замысловатым способом мы реализуем FULL JOIN --->
UNION ALL
SELECT NULL, m.code, NULL, NULL, NULL, NULL, 0,
NULL, NULL, m.raw_metric, m.metric, NULL, NULL, NULL, NULL, m.dt, 0, m.dimension_1, m.dimension_1_name, m.dimension_2, m.dimension_2_name
FROM qUnifiedMetric as m
WHERE m.code NOT IN (<cfqueryparam value="#ValueList(qSpec.code)#" cfsqltype="cf_sql_varchar" list="true">)
UNION ALL
SELECT s.code, NULL, s.service_code, s.svc, s.component, s.unit, s.price*(100-s.discount)/100 as discounted_price,
s.price, s.discount, NULL, NULL, s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, NULL, 0, NULL, NULL, NULL, NULL
FROM qSpec as s
WHERE s.code NOT IN (<cfqueryparam value="#listRemoveDuplicates(ValueList(qUnifiedMetric.code))#" cfsqltype="cf_sql_varchar" list="true">)
ORDER by dt, s_code, m_code, dimension_1, dimension_2
</cfquery>
<!--- диагностика ошибок в QoQ крайне невнятна и вводит в заблуждение --->
<!---<cfdump var=#qChargeDaily# abort=true/> --->
<cfif (ATTRIBUTES.debug)>
qChargeDaily
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qChargeDaily#/>
<cfflush/>
</cfif>
<!--- Из-за детализации придется вынести вычитание free_tier на более поздний этап --->
<!--- Здесь агрегировано за месяц, но детализировано по измерениям --->
<cfquery name="qChargeMonthly" dbType="query">
SELECT
<d:field_set titleMapOut="chargeTitleMap" lengthOut="chargeFieldCount">
<d:field title="Артикул по договору">s_code</d:field>
<d:field title="Артикул по системам">m_code</d:field>
<d:field title="Артикул">s_code as code</d:field>
<d:field title="Код услуги">service_code</d:field>
<d:field title="Услуга по каталогу">svc</d:field>
<d:field title="serviceCustomName">serviceCustomName</d:field>
<d:field title="Компонент">component</d:field>
<d:field title="Ед.изм.">unit</d:field>
<d:field title="Цена &##8381; с НДС" cfSqlType="CF_SQL_NUMERIC">discounted_price</d:field>
<d:field title="Сырая метрика" cfSqlType="CF_SQL_NUMERIC">sum(raw_metric) as raw_metric</d:field>
<d:field title="Приведенный объем" cfSqlType="CF_SQL_NUMERIC">sum(metric) as metric</d:field>
<d:field title="serviceStart" cfSqlType="CF_SQL_TIMESTAMP">serviceStart</d:field>
<d:field title="serviceEnd" cfSqlType="CF_SQL_TIMESTAMP">serviceEnd</d:field>
<d:field title="dt_billed_from" cfSqlType="CF_SQL_TIMESTAMP">min(dt) as dt_billed_from</d:field>
<d:field title="dt_billed_to" cfSqlType="CF_SQL_TIMESTAMP">max(dt) as dt_billed_to</d:field>
<d:field title="serviceHash">serviceHash</d:field>
<d:field title="free tier" cfSqlType="CF_SQL_NUMERIC">free_tier</d:field>
<d:field>dimension_1</d:field>
<d:field>dimension_1_name</d:field>
<d:field>dimension_2</d:field>
<d:field>dimension_2_name</d:field>
<d:field title="Год" cfSqlType="CF_SQL_INTEGER">year(dt) as year</d:field>
<d:field title="Месяц" cfSqlType="CF_SQL_INTEGER">month(dt) as month</d:field>
</d:field_set>
from qChargeDaily
group by s_code, m_code, svc, service_code, serviceCustomName, component, unit, discounted_price, serviceStart, ServiceEnd, serviceHash, free_tier, year(dt), month(dt),
dimension_1_name, dimension_1, dimension_2_name, dimension_2
order by year(dt), month(dt), m_code, code, serviceStart, dimension_1, dimension_2
</cfquery>
<!--- в связи с бедностью языка QoQ (нет CASE) грубо патчим резалтсет --->
<cfloop query="qChargeMonthly">
<cfset qChargeMonthly.code = (len(code) GT 0) ? code : m_code/><!--- имя в левой части должно быть квалифицировано запросом (резалтсетом) --->
</cfloop>
<!--- тут могут быть подводные камни с копированием и переиспользованием объектов --->
<!--- <cfif isQuery(qTotal)>
<cfquery name="qTotalNew" dbType="query">
select * from qTotal
union all
select * from qCharge
</cfquery>
<cfset qTotal = qTotalNew/>
<cfelse>
<cfset qTotal = qCharge/>
</cfif>
<cfdump var=#qTotal#/> --->
<!--- <cfdump var=#qChargeMonthly#/> --->
<!--- *** проверить краевые даты --->
<cfif (ATTRIBUTES.debug)>
qChargeMonthly
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qChargeMonthly#/>
<cfflush/>
</cfif>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<!--- <cfflush/> --->
<cfset var report={}/>
<cfset report.qComputingAge = qComputingAge/>
<cfset report.qStorageAge = qStorageAge/>
<cfset report.qS3VolAge = qS3VolAge/>
<cfset report.qS3OpsTrfAge = qS3OpsTrfAge/>
<cfset report.qSpecification = qSpecification/>
<cfset report.qCharge = qChargeMonthly/>
<cfset report.qFreeTier = qFreeTier/><!--- мы не применяем скидку на уровне детализации, а только после агрегации. На самом деле правильно было бы агрегировать натуральные метрики, а умножать на цену уже агрегаты --->
<cfset report.chargeTitleMap = chargeTitleMap/><!--- нужен для экспорта в XL --->
<!--- <cfset report.chargeFieldCount = chargeFieldCount/> --->
<cfset "CALLER.#ATTRIBUTES.output#" = report/>
<cfexit method="exittag"/>
+3
View File
@@ -0,0 +1,3 @@
White label
на экране логина предусматривать место для custom текста
Помимо лого, предусматривать custom text
+109
View File
@@ -0,0 +1,109 @@
<cfimport prefix="m" taglib="../lib"/>
<cfimport prefix="d" taglib="../lib/data"/>
<cfparam name="ATTRIBUTES.wz"/>
<!--- <cfparam name="ATTRIBUTES.crm_cache_base" default="https://elma-cache.adl.nubes.ru/contract_ls.cfm"/> --->
<cfparam name="ATTRIBUTES.out" default="qComponent"/>
<cfhttp method="get"
url="#request.crm_cache_base#?wz=#ATTRIBUTES.wz#"
timeout=10
charset="utf-8"
result="res"
>
</cfhttp>
<cfset local={}/>
<!--- --->
<cfset safeJsonString = REReplace(res.fileContent, '(:\s*)(-?\d+\.\d{16,})', '$1"$2"', "ALL")/><!--- очень длинные числа завернуть в кавычки --->
<!--- <cfdump var=#safeJsonString#/> --->
<cftry>
<cfset company=deserializeJson(safeJsonString)/>
<cfcatch type="ANY">
Cannot deserialize CRM response
result:
<cfdump var=#res#/>
safeJsonString:
<cfdump var=#safeJsonString#/>
</cfcatch>
</cftry>
<!---<cfset company=deserializeJson(res.fileContent)/> --->
<!---<cfdump var=#company#/> --->
<!--- *** это не слишком надежно, некорректно представленный NULL (обыкновенно представляемый пустой строкой) может все сломать. Да и корректный --->
<!--- мартышкин труд собирать этот запрос из JSON , может, лучше было динамически --->
<cfset qComponent = queryNew("
contractId
,contractOutsideId
,additionalAgreementId
,additionalAgreementName
,dealIndex
,serviceId
,serviceType
,catalogName
,serviceCode
,serviceCustomName
,serviceStart
,serviceEnd
,serviceDiscount
,serviceQuantity
,serviceHash
,paramId
,componentCode
,component
,measureShort
,quantity
,priceWVat
,discount
,atomicCode
,fullCode
",
"varChar,varChar,varChar,varChar,integer,varChar,integer,varChar,varChar,varChar,timestamp,timestamp,decimal,decimal,varChar,integer,varChar,varChar,varChar,decimal,decimal,decimal,varChar,varChar")/>
<!--- если не указывать типы, он пытается их определить сам, потом проблемы с union и арифметикой --->
<!--- this.nullSupport = true in Application.cfc ... no way --->
<cfloop array=#company.contracts# item="local.contract"><!--- имя массива писать в ##, иначе думает, что это строковый литерал --->
<cfloop array=#local.contract.additionalAgreements# item="local.additionalAgreement">
<cfif NOT local.additionalAgreement.isActual><cfcontinue/></cfif>
<cfloop array=#local.additionalAgreement.services# item="local.service">
<cfloop array=#local.service.components# item="local.component">
<cfset queryAddRow(qComponent, [{
contractId=local.contract.contractId,
contractOutsideId=local.contract.contractOutsideId,
additionalAgreementId=local.additionalAgreement.additionalAgreementId,
additionalAgreementName=local.additionalAgreement.additionalAgreementName,
dealIndex=local.service.dealIndex ,
serviceId=local.service.serviceId ,
serviceType=local.service.serviceType,
catalogName=local.service.catalogName,
serviceCode=local.service.serviceCode,
serviceCustomName=local.service.serviceCustomName,
serviceStart=parseDateTime(local.service.serviceStart.ts),
serviceEnd=isDate(structGet("local.service.serviceEnd.ts"))? parseDateTime(local.service.serviceEnd.ts):javacast('null',''),
serviceDiscount=local.service.discount,
serviceQuantity=local.service.quantity,
serviceHash=local.service.serviceHash,
paramId=local.component.paramId,
componentCode=local.component.componentCode,
component=local.component.component,
measureShort=local.component.measureShort,
quantity=local.component.quantity,
priceWVat=local.component.priceWVat,
discount=local.component.discount,
atomicCode=local.component.atomicCode,
fullCode="#local.component.componentCode#-#listGetAt('i,f,m',local.service.serviceType)#"
}])/>
</cfloop>
</cfloop>
</cfloop>
</cfloop>
<!--- <cfdump var=#qComponent#/> --->
<cfset "CALLER.#ATTRIBUTES.out#" = qComponent/>
<cfexit method="exittag"/>
+22
View File
@@ -0,0 +1,22 @@
SQL SELECT
s.code,m.code as m_code,s.service_code as service_code,s.svc,s.component,s.unit,
s.price*(100-s.discount)/100 as discounted_price
,s.price,s.discount,m.raw_metric,m.metric,s.serviceStart,s.serviceEnd,s.serviceHash,s.serviceCustomName,m.dt,s.free_tier,m.dimension_1,m.dimension_1_name ||'x' as dimension_1_name,m.dimension_2,m.dimension_2_name
FROM qSpec as s, qUnifiedMetric as m
WHERE m.wz='62'
AND s.code=m.code
AND s.serviceStart <= m.dt
AND (m.dt <= s.serviceEnd OR s.serviceEnd IS NULL)
UNION ALL
SELECT NULL, m.code, NULL, NULL, NULL, NULL, 0,
NULL, NULL, m.raw_metric, m.metric, NULL, NULL, NULL, NULL, m.dt, 0, m.dimension_1, m.dimension_1_name ||'y', m.dimension_2, m.dimension_2_name
FROM qUnifiedMetric as m where m.code NOT IN ('5','6')
UNION ALL
SELECT s.code, NULL, s.service_code, s.svc, s.component, s.unit, s.price*(100-s.discount)/100 as discounted_price,
s.price, s.discount, NULL, NULL, s.serviceStart, s.serviceEnd, s.serviceHash, s.serviceCustomName, NULL, 0, NULL, NULL, NULL, NULL
FROM qSpec as s where s.code NOT IN ('5','6')
order by m.dt, m.code, dimension_1, dimension_2
+77 -12
View File
@@ -5,25 +5,46 @@
<cfparam name="ATTRIBUTES.dt_start" type="date" default=#dateAdd('m',-1,ATTRIBUTES.dt_finish)#/> <cfparam name="ATTRIBUTES.dt_start" type="date" default=#dateAdd('m',-1,ATTRIBUTES.dt_finish)#/>
<cfparam name="ATTRIBUTES.output" type="string" default="report"/> <cfparam name="ATTRIBUTES.output" type="string" default="report"/>
<cfparam name="ATTRIBUTES.debug" type="boolean" default=false/> <cfparam name="ATTRIBUTES.debug" type="boolean" default=false/>
<cfparam name="ATTRIBUTES.tenant_wz_index" type="string"/>
<cfset hours=dateDiff('h', ATTRIBUTES.dt_start, ATTRIBUTES.dt_finish)/> <cfset hours=dateDiff('h', ATTRIBUTES.dt_start, ATTRIBUTES.dt_finish)/>
<cfset tenant_wz_index=val(mid(request.auth.wz,3,5))/><!--- cut off WZ --->
<!--- QoQ does not support join syntax, at least of Lucee 5.4--->
<!--- QoQ does not support join syntax, and of course outer joins, at least of Lucee 5.4--->
<!--- а внешние джойны хорошо подсвечивают ошибки, особенно помогает full --->
<cfmodule template="crm_contract.cfm" wz="WZ01348" out="qSpecification"/>
<cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qSpecification#/>
<cfflush/>
</cfif>
<!--- Для начала выкатываем просто кусок каталога, не фильтруя по спеке (которой иногда нет) ---> <!--- Для начала выкатываем просто кусок каталога, не фильтруя по спеке (которой иногда нет) --->
<cfquery name="qSpec"> <!--- <cfquery name="qSpec">
select * from ( select * from (
values values
('Объектное хранилище S3 CEPH','Дисковое пространство: Объем хранения','paas.s3.ceph.vol-m','ГБайт',1.80,0) ('Объектное хранилище S3 CEPH','Дисковое пространство: Объем хранения (HOT)','paas.s3.ceph.vol-m','ГБайт',1.80,0)
,('Объектное хранилище S3 CEPH','Исходящий трафик','paas.s3.ceph.trf-m','ГБайт',1.50,0) ,('Объектное хранилище S3 CEPH','Исходящий трафик (HOT)','paas.s3.ceph.trf-m','ГБайт',1.50,0)
,('Объектное хранилище S3 CEPH','Запросы GET, HEAD, OPTIONS','paas.s3.ceph.get-m','10тыс.шт.',0.35,0) ,('Объектное хранилище S3 CEPH','Запросы GET, HEAD, OPTIONS (HOT)','paas.s3.ceph.get-m','10тыс.шт.',0.35,0)
,('Объектное хранилище S3 CEPH','Запросы PUT, POST, PATCH','paas.s3.ceph.put-m','10тыс.шт.',4.50,0) ,('Объектное хранилище S3 CEPH','Запросы PUT, POST, PATCH (HOT)','paas.s3.ceph.put-m','10тыс.шт.',4.50,0)
,('Объектное хранилище S3 CEPH','Дисковое пространство: Объем хранения (COLD)','paas.s3.cphc.vol-m','ГБайт',0.84*1.2,0)
,('Объектное хранилище S3 CEPH','Исходящий трафик (COLD)','paas.s3.cphc.trf-m','ГБайт',1.25*1.2,0)
,('Объектное хранилище S3 CEPH','Запросы GET, HEAD, OPTIONS (COLD)','paas.s3.cphc.get-m','10тыс.шт.',0.8*1.2,0)
,('Объектное хранилище S3 CEPH','Запросы PUT, POST, PATCH (COLD)','paas.s3.cphc.put-m','10тыс.шт.',9.7*1.2,0)
) as ) as
s (svc, component, code, unit, price, discount) s (svc, component, code, unit, price, discount)
order by code order by code
</cfquery> --->
<!--- здесь нужно организовать пересечение версионной спецификации с анализируемым диапазоном дат --->
<cfquery name="qSpec" dbtype="query">
select catalogName as svc, component, fullCode as code, measureShort as unit
, priceWVat as price, (discount + serviceDiscount - discount*serviceDiscount/100) as discount
,serviceStart, serviceEnd
from qSpecification
</cfquery> </cfquery>
@@ -41,7 +62,7 @@
from ngcloud_ru.capacity_resource from ngcloud_ru.capacity_resource
join ngcloud_ru.vdc on capacity_resource.vdc_id=vdc.id join ngcloud_ru.vdc on capacity_resource.vdc_id=vdc.id
join ngcloud_ru.tenant on tenant.id=vdc.tenant_id join ngcloud_ru.tenant on tenant.id=vdc.tenant_id
where tenant.wzcode = <cfqueryparam cfsqltype="cf_sql_integer" value=#tenant_wz_index#/> where tenant.wzcode = <cfqueryparam cfsqltype="cf_sql_integer" value=#ATTRIBUTES.tenant_wz_index#/>
AND capacity_resource.timestamp >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_start#/> AND capacity_resource.timestamp >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_start#/>
AND capacity_resource.timestamp < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_finish#/> AND capacity_resource.timestamp < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_finish#/>
group by tenant.wzcode, vdc.name group by tenant.wzcode, vdc.name
@@ -49,6 +70,7 @@
</cfquery> </cfquery>
<cfif (ATTRIBUTES.debug)> <cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qComputing#/> <cfdump var=#qComputing#/>
<cfflush/> <cfflush/>
</cfif> </cfif>
@@ -57,6 +79,12 @@
select max(capacity_resource.timestamp) as dt_load from ngcloud_ru.capacity_resource select max(capacity_resource.timestamp) as dt_load from ngcloud_ru.capacity_resource
</cfquery> </cfquery>
<cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qComputingAge#/>
<cfflush/>
</cfif>
<!--- переделать storage_profile_types с расшифровкой ssd, sata, можно прямо до каталожного кода ---> <!--- переделать storage_profile_types с расшифровкой ssd, sata, можно прямо до каталожного кода --->
<cfquery name="qStorage"> <cfquery name="qStorage">
@@ -80,7 +108,7 @@
join ngcloud_ru.vdc on capacity_storage.vdc_id=vdc.id join ngcloud_ru.vdc on capacity_storage.vdc_id=vdc.id
join ngcloud_ru.tenant on tenant.id=vdc.tenant_id join ngcloud_ru.tenant on tenant.id=vdc.tenant_id
join ngcloud_ru.storage_profile_types on capacity_storage.storage_profile_types_id=storage_profile_types.id join ngcloud_ru.storage_profile_types on capacity_storage.storage_profile_types_id=storage_profile_types.id
where tenant.wzcode = <cfqueryparam cfsqltype="cf_sql_integer" value=#tenant_wz_index#/> where tenant.wzcode = <cfqueryparam cfsqltype="cf_sql_integer" value=#ATTRIBUTES.tenant_wz_index#/>
AND capacity_storage.timestamp >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_start#/> AND capacity_storage.timestamp >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_start#/>
AND capacity_storage.timestamp < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_finish#/> AND capacity_storage.timestamp < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_finish#/>
group by tenant.wzcode, vdc.name, storage_profile_types.name group by tenant.wzcode, vdc.name, storage_profile_types.name
@@ -91,6 +119,7 @@
</cfquery> </cfquery>
<cfif (ATTRIBUTES.debug)> <cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qStorage#/> <cfdump var=#qStorage#/>
<cfflush/> <cfflush/>
</cfif> </cfif>
@@ -118,7 +147,7 @@ HOT_FREE_LIMIT 1 10 1 100
join s3billing.placement ON s3billing.placement.id=bucket_stat.placement_id join s3billing.placement ON s3billing.placement.id=bucket_stat.placement_id
join s3billing.bucket_info ON bucket_info.id=bucket_stat.bucket_id join s3billing.bucket_info ON bucket_info.id=bucket_stat.bucket_id
where bucket_stat.placement_id in (1,2) where bucket_stat.placement_id in (1,2)
AND bucket_stat.owner = <cfqueryparam cfsqltype="cf_sql_varchar" value=#tenant_wz_index#/> AND bucket_stat.owner = <cfqueryparam cfsqltype="cf_sql_varchar" value=#ATTRIBUTES.tenant_wz_index#/>
AND bucket_stat.timestamp_addition >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_start#/> AND bucket_stat.timestamp_addition >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_start#/>
AND bucket_stat.timestamp_addition < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_finish#/> AND bucket_stat.timestamp_addition < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_finish#/>
group by bucket_stat.owner, bucket_stat.placement_id group by bucket_stat.owner, bucket_stat.placement_id
@@ -126,6 +155,7 @@ HOT_FREE_LIMIT 1 10 1 100
</cfquery> </cfquery>
<cfif (ATTRIBUTES.debug)> <cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qS3Vol#/> <cfdump var=#qS3Vol#/>
<cfflush/> <cfflush/>
</cfif> </cfif>
@@ -134,6 +164,12 @@ HOT_FREE_LIMIT 1 10 1 100
select max(timestamp_addition) as dt_load from s3billing.bucket_stat select max(timestamp_addition) as dt_load from s3billing.bucket_stat
</cfquery> </cfquery>
<cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qS3VolAge#/>
<cfflush/>
</cfif>
<cfquery name="qS3OpsTrf"> <cfquery name="qS3OpsTrf">
select select
sum( sum(
@@ -203,7 +239,7 @@ select
join s3billing.bucket_info on usage_bucket_by_user.bucketid=bucket_info.id join s3billing.bucket_info on usage_bucket_by_user.bucketid=bucket_info.id
join s3billing.placement on bucket_info.placement_id=placement.id join s3billing.placement on bucket_info.placement_id=placement.id
where 1=1 where 1=1
AND user_info.name = <cfqueryparam cfsqltype="cf_sql_varchar" value=#tenant_wz_index#/> AND user_info.name = <cfqueryparam cfsqltype="cf_sql_varchar" value=#ATTRIBUTES.tenant_wz_index#/>
AND usage_bucket_by_user.time >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_start#/> AND usage_bucket_by_user.time >= <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_start#/>
AND usage_bucket_by_user.time < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_finish#/> AND usage_bucket_by_user.time < <cfqueryparam cfsqltype="cf_sql_timestamp" value=#ATTRIBUTES.dt_finish#/>
group by user_info.name, bucket_info.placement_id group by user_info.name, bucket_info.placement_id
@@ -211,6 +247,7 @@ select
</cfquery> </cfquery>
<cfif (ATTRIBUTES.debug)> <cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qS3OpsTrf#/> <cfdump var=#qS3OpsTrf#/>
<cfflush/> <cfflush/>
</cfif> </cfif>
@@ -219,7 +256,13 @@ select
select max(time) as dt_load from s3billing.usage_bucket_by_user select max(time) as dt_load from s3billing.usage_bucket_by_user
</cfquery> </cfquery>
<cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qS3OpsTrfAge#/>
<cfflush/>
</cfif>
<!--- собрали все метрики в один резалтсет --->
<cfquery name="qUnifiedMetric" dbType="query"> <cfquery name="qUnifiedMetric" dbType="query">
select wz, code || '.vcpu-m' as code, core_h as raw_metric, core_h as metric, core_h as chargeable_metric from qComputing select wz, code || '.vcpu-m' as code, core_h as raw_metric, core_h as metric, core_h as chargeable_metric from qComputing
union all union all
@@ -237,14 +280,30 @@ select wz, code || '.trf-m', bytes_sent as raw_metric, bytes_sent_gb as metric,
</cfquery> </cfquery>
<cfif (ATTRIBUTES.debug)> <cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qUnifiedMetric#/> <cfdump var=#qUnifiedMetric#/>
<cfflush/> <cfflush/>
</cfif> </cfif>
<!--- умножаем на цену, вычисляем дисконт (довольно поздно) и форматируем
*** здесь нужно учесть версионность. Самым простым кажется декомпозировать по дням и снова собрать
*** только как нам это сделать без нормального SQL движка
Может быть, процедурная обработка не будет выглядеть ужасно, и не хуже SQL
В конце концов, можно просто В ЦИКЛЕ обсчитать диапазон по дням, выполнив запрос 30 раз, или сколько надо
Если будет тупить - оптимизируем
--->
<!--- Для декомпозиции на более мелкие объекты мы можем как полумеру - профильтровать по конкретному бакету или ВМ-ВДЦ
Что не нравится - для ВМ у нас может быть целое дерево нижележащих объектов --->
<cfloop from=#attribute.dt_start# to=#attribute.dt_finish# index="dt">
</cfloop>
<cfquery name="qCharge" dbType="query"> <cfquery name="qCharge" dbType="query">
SELECT SELECT
<d:field_set titleMapOut="titleMap" lengthOut="fieldCount"> <d:field_set titleMapOut="titleMap" lengthOut="fieldCount">
<d:field title="Артикул">s.code</d:field> <d:field title="Артикул">s.code</d:field>
<d:field title="Артикул">m.code as m_code</d:field>
<d:field title="Услуга">s.svc</d:field> <d:field title="Услуга">s.svc</d:field>
<d:field title="Компонент">s.component</d:field> <d:field title="Компонент">s.component</d:field>
<d:field title="Кол-во (метрика)" cfSqlType="CF_SQL_NUMERIC">m.chargeable_metric</d:field> <d:field title="Кол-во (метрика)" cfSqlType="CF_SQL_NUMERIC">m.chargeable_metric</d:field>
@@ -254,7 +313,7 @@ SELECT
<!--- <d:field title="Дата НОУ" cfSqlType="CF_SQL_TIMESTAMP">s.dt_from</d:field> ---> <!--- <d:field title="Дата НОУ" cfSqlType="CF_SQL_TIMESTAMP">s.dt_from</d:field> --->
<!--- <d:field title="Дата оконч." cfSqlType="CF_SQL_TIMESTAMP">s.dt_to</d:field> <!--- <d:field title="Дата оконч." cfSqlType="CF_SQL_TIMESTAMP">s.dt_to</d:field>
<d:field title="Ключ строки">s.line_key</d:field> ---> <d:field title="Ключ строки">s.line_key</d:field> --->
<d:field title="GPL с НДС" cfSqlType="CF_SQL_NUMERIC">s.price</d:field> <d:field title="Цена с НДС" cfSqlType="CF_SQL_NUMERIC">s.price</d:field>
<d:field title="Скидка%" cfSqlType="CF_SQL_NUMERIC">s.discount</d:field> <d:field title="Скидка%" cfSqlType="CF_SQL_NUMERIC">s.discount</d:field>
<d:field title="Сырая метрика" cfSqlType="CF_SQL_NUMERIC">m.raw_metric</d:field> <d:field title="Сырая метрика" cfSqlType="CF_SQL_NUMERIC">m.raw_metric</d:field>
<d:field title="Приведенная метрика" cfSqlType="CF_SQL_NUMERIC">m.metric</d:field> <d:field title="Приведенная метрика" cfSqlType="CF_SQL_NUMERIC">m.metric</d:field>
@@ -266,6 +325,12 @@ WHERE m.wz=<cfqueryparam cfsqltype="cf_sql_varchar" value=#request.auth.wz#/> AN
order by s.code order by s.code
</cfquery> </cfquery>
<cfif (ATTRIBUTES.debug)>
<cfoutput>#getTickCount() - request.startTickCount#</cfoutput>
<cfdump var=#qCharge#/>
<cfflush/>
</cfif>
<cfset var report={}/> <cfset var report={}/>
<cfset report.qComputingAge = qComputingAge/> <cfset report.qComputingAge = qComputingAge/>
+318
View File
@@ -0,0 +1,318 @@
<cfsilent>
<cfimport prefix="m" taglib="lib"/>
<cfimport prefix="c" taglib="lib/controls"/>
<cfimport prefix="d" taglib="lib/data"/>
<cfimport prefix="layout" taglib="layout"/>
</cfsilent>
<m:prepare_ls entity="payg" accessObject="" pageInfoOut="pageInfo" trackOut="tr"/>
<cfparam name="dt_finish" type="date" default=#dateAdd('d',-1,createDateTime(year(Now()),month(Now()),1,0,0,0))#/>
<cfparam name="dt_start" type="date" default=#dateAdd('m',-1,createDateTime(year(Now()),month(Now()),1,0,0,0))#/>
<cfset hours=dateDiff('h', dt_start, dateAdd('d', 1, dt_finish))/>
<cfset restrict_to_own_instances = true>
<cfparam name="WZ" default=#request.auth.wz#/>
<cfif request.auth.wz EQ 'WZ01112'><!--- грубо --->
<cfset restrict_to_own_instances = false>
<cfelse>
<cfset WZ=request.auth.wz/><!--- без баловства --->
</cfif>
<cfset tenant_wz_index=val(mid(WZ,3,5))/><!--- cut off WZ --->
<cfif isDefined("output_xls")><cfmodule template="mod/compute_payg.cfm"
dt_finish=#dt_finish#
dt_start=#dt_start#
tenant_wz_index=#tenant_wz_index#
output="report"
debug=#isDefined("DEBUG")#
restrict_to_own_instances=#restrict_to_own_instances#
/>
<layout:xml qRead=#report.qCharge# titleMap=#report.chargeTitleMap# filename="#pageInfo.entity#.xml"/>
<cfabort/>
</cfif><cfif isDefined("output_json")><cfmodule template="mod/compute_payg.cfm"
dt_finish=#dt_finish#
dt_start=#dt_start#
tenant_wz_index=#tenant_wz_index#
output="report"
debug=#isDefined("DEBUG")#
restrict_to_own_instances=#restrict_to_own_instances#
/><!--- <cfdump var=#report#/><cfabort/> ---><layout:json qRead=#report.qCharge# titleMap=#report.chargeTitleMap# filename="#pageInfo.entity#.json"/>
<cfabort/>
</cfif><!---
---><layout:page section="header" pageInfo=#pageInfo#>
<layout:attribute name="title">
<cfoutput><b>Отчет по PAYG</b> #WZ#</cfoutput>
</layout:attribute>
<layout:attribute name="controls">
<!---skip filter link, filter is not implemented--->
<!---<layout:language_switch/>--->
</layout:attribute>
</layout:page>
<!--- Внимание! округление вверх при расчете метрик get, put S3. Расчетные суммы могут содержать погрешности округления, поэтому окончательный расчет стоимости (количество * цену) выполняeтся в бухгалтерской программе.
<b>Если у клиента больше 1 действующего допника с услугами PAYG, то данные в отчете могут быть замножены, в этом случае считать вручную</b> --->
<cfoutput>
<form method="post" action="#request.thisPage#" style="display:inline"><!--- не надо делать action="" - параметр, затесавшийся в URL будет затирать параметр формы, и заметить это трудно --->
Период с <input type="text" name="dt_start" value="#dateFormat(dt_start,'YYYY-MM-DD')#"/>
по <input type="text" name="dt_finish" value="#dateFormat(dt_finish,'YYYY-MM-DD')#"/>
<cfif request.auth.wz EQ 'WZ01112'><!--- очень грубо --->
WZ <input type="text" name="wz" value="#WZ#"/>
</cfif>
<input type="submit" style="cursor:pointer;"/>
<input type="submit" name="DEBUG" value="DEBUG" style="cursor:pointer;"/><!--- ********* --->
</form>
<cfset month_start = createDate(year(now()), month(now()), 1)/>
<a href="?dt_start=#dateFormat(dateAdd('m', -1, month_start),'YYYY-MM-DD')#&dt_finish=#dateFormat(dateAdd('d', -1, month_start),'YYYY-MM-DD')#&wz=#WZ#">
прошлый месяц
</a>
<a href="?dt_start=#dateFormat(createDate(year(now()), month(now()), 1),'YYYY-MM-DD')#&dt_finish=#dateFormat(createDate(year(now()), month(now()), daysInMonth(now())),'YYYY-MM-DD')#&wz=#WZ#">
текущий месяц
</a>
<br/>
<!--- <cfflush/> --->
<cfmodule template="mod/compute_payg.cfm"
dt_finish=#dt_finish#
dt_start=#dt_start#
tenant_wz_index=#tenant_wz_index#
output="report"
debug=#structKeyExists(Form,"DEBUG")#
restrict_to_own_instances=#restrict_to_own_instances#
/>
Часов в периоде: <b>#hours#</b> Строк в отчете: <b>#report.qCharge.recordCount#</b><br/>
Актуальность данных:
VCD Computing: <b<cfif datediff("h",report.qComputingAge.dt_load, Now()) GT 24> class="err"</cfif>>
#dateFormat(report.qComputingAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qComputingAge.dt_load,'HH:MM:SS')#
</b>
VCD Storage: <b<cfif datediff("h",report.qStorageAge.dt_load, Now()) GT 24> class="err"</cfif>>
#dateFormat(report.qStorageAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qStorageAge.dt_load,'HH:MM:SS')#</b>
S3 хранение: <b<cfif datediff("h",report.qS3VolAge.dt_load, Now()) GT 24> class="err"</cfif>>
#dateFormat(report.qS3VolAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3VolAge.dt_load,'HH:MM:SS')#
</b>
S3 операции и трафик: <b<cfif datediff("h",report.qS3OpsTrfAge.dt_load, Now()) GT 24> class="err"</cfif>>
#dateFormat(report.qS3OpsTrfAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3OpsTrfAge.dt_load,'HH:MM:SS')#
</b>
<br/>
Цены по данным договора из CRM (через elma-cache). Учитываются метрики по S3, vCloud (кроме GPU). Внимание! количество за период *** округляется вверх, но делается это после всех агрегаций.
<br/>
<a href="#request.thisPage#?output_xls" title="экспорт в Excel" style="margin-left:.5em; height:100%;" target="_blank"><img src="img/xls.gif" style="vertical-align:text-bottom;"/></a>
<a href="#request.thisPage#?output_json" title="экспорт в json" style="margin-left:.5em; height:100%;" target="_blank"><img src="img/json.svg" style="vertical-align:text-bottom;" width="13" height="13"/></a>
</cfoutput>
<table class="worktable">
<thead>
<th width="1%"></th>
<th width="7%" colspan="2">Артикул</th>
<th width="3%">С</td>
<th width="3%">По</td>
<th width="3%">Кол-во (метрика)</td>
<th width="3%">Free Tier</td>
<th width="7%">Оплачиваемый объем</th>
<th width="3%">Ед.изм.</th>
<th width="3%">Цена &#8381; с НДС</th>
<th width="3%">Стоимость &#8381; с НДС</th>
<th width="10%">Услуга</th>
<th width="10%">Компонент</th>
<!--- <td width="10%">Клиентс. назв.</td> --->
<!--- <td width="3%">GPL с НДС</td>
<td width="3%">Скидка%</td> --->
<!--- <td width="6%">Дата НОУ</td>
<td width="6%">Дата оконч.</td>
<td width="3%">Ключ строки</td> --->
<!--- <td width="3%">Сырая метрика</td> --->
<!--- <td width="5%">Договор</td>
<td width="10%">Допник</td>
<td width="10%">Сделка</td> --->
</thead>
<!--- *** Обнаружена засада: возможно, группировка (сортировка) отображаемого вывода нужна по размерности детализации, а не по коду
Но: агрегация скорее нужна по коду
Видимо, придется на фронте перетасовывать?
--->
<!--- Еще засада: если я делегирую агрегацию на фронт, мне нужно и вычет free_tier делегировать на фронт (помня еще, что он на месяц, а не на произвольный период - наверно, можно его вычитать всегда, а больше месяца не агрегировать... или умножать на число полных месяцев) --->
<!--- видимо, Free Tier и единицу измерения нужно брать и для строк, которые не найдены в договоре
(еше можно было бы туда для полноты добавлять GPL цену, но для этого нужен каталог)
Добавить реквизиты договора? --->
<cfoutput query="report.qCharge" group="year">
<cfoutput group="month">
<tr><td colspan="99"><cfif month GT 0><b>#year#-#numberFormat(month,"00")#</b></cfif></td></tr>
<cfoutput group="svc">
<cfset var accum_cost_svc = 0/>
<cfset var accum_metric_svc = 0/>
<tr>
<td bgcolor="##ccc"></td>
<td bgcolor="##ccc" colspan="12">
#svc# <b>#service_code#</b>
<cfif len(serviceCustomName)>(#serviceCustomName#)</cfif>
<cfif serviceHash EQ "">(не найдено в договоре)</cfif>
</td>
</tr>
<cfoutput group="code">
<cfoutput group="serviceStart"> <!--- может быть, для ясности надо в каждой строке писать точный период --->
<cfset accum_metric_svc += isNumeric(metric) ? metric : 0/><!--- агрегация по метрике возможна только на уровне компонента --->
<tr>
<td></td>
<td colspan="10">#code# <cfif code EQ "">(#m_code#)</cfif></td>
<td>#svc#</td>
<td>#component# </td>
</tr>
<cfset accum_metric_dimension_1 = 0/>
<cfoutput group="dimension_1">
<cfset accum_metric_dimension_1 += isNumeric(metric) ? metric : 0/>
<tr class="i">
<td>#currentRow#</td>
<td>#dimension_1_name#</td>
<td>#dimension_1#</td>
<td class="c">#dateFormat(dt_billed_from,"YYYY-MM-DD")#</td>
<td class="c">#dateFormat(dt_billed_to,"YYYY-MM-DD")#</td>
<td class="r"><cfif metric NEQ 0 AND isNumeric(metric)>#round(metric,3)#</cfif></td>
<td><!--- free tier ---></td>
<td class="r" style="font-size:120%; padding:0 1em;"></td>
<td class="c">#unit#</td>
<td class="r">#gaussianRound(discounted_price,2)#</td>
<td class="r"><!--- здесь не вычисляется стоимость, потому что Free Tier вычитается после агрегации (скидочные политики реализовывать будет непросто) ---></td>
<td></td>
<td></td>
</tr>
</cfoutput>
<cfset cost_dimension_1 = precisionEvaluate("gaussianRound(max(accum_metric_dimension_1 - free_tier, 0)*gaussianRound(discounted_price, 2), 2)")/>
<tr class="b">
<td></td>
<td colspan="4">Итого #code# <cfif code EQ "">(#m_code#)</cfif>:</td>
<td class="r"><cfif accum_metric_dimension_1 NEQ 0 AND isNumeric(accum_metric_dimension_1)>#round(accum_metric_dimension_1,3)#</cfif></td>
<td class="r">#free_tier#</td>
<td class="r" style="font-size:120%; padding:0 1em;"><cfif accum_metric_dimension_1 NEQ 0 AND isNumeric(accum_metric_dimension_1)>#max(ceiling(accum_metric_dimension_1) - free_tier, 0)#</cfif></td>
<td class="c">#unit#</td>
<td class="r">#gaussianRound(discounted_price,2)#</td><!--- если цена менялась в периоде, неправильно показыввать последнюю - но мы не тестировали с изменением в периоде --->
<!--- непонятно, как вычитать Free Tier для неполных месяцев. Если с 15 по 15 - вычитать 2 раза, вычитать (каким-то сложным образом) 1 раз или ни разу --->
<!--- видимо, нужно сформировать тест кейсы на неполные месяцы. Где взять биллинговые данные для этого ВПР --->
<td class="r">#cost_dimension_1#</td>
<cfset accum_cost_svc = precisionEvaluate("accum_cost_svc + cost_dimension_1")/><!--- простое сложение дает ошибку округления и лишние знаки, но и precisionEvaluate не выглядит слишком надежным --->
<td>#svc# <!--- #service_code# ---></td>
<td>#component# </td>
</tr>
</cfoutput><!--- serviceStart --->
</cfoutput><!--- code --->
<tr>
<td bgcolor="##eee" style="border-bottom:2px solid gray;"></td>
<td bgcolor="##eee" colspan="9" class="r" style="border-bottom:2px solid gray;">
<b>Итого </b> &nbsp;&nbsp;&nbsp;
<cfif len(service_code)>
#svc# <b>#service_code#</b>
<cfelse>
(#m_code#)
</cfif>
<cfif month GT 0>за #year#-#numberFormat(month,"00")#</cfif>:
</td>
<td bgcolor="##eee" class="r b" style="border-bottom:2px solid gray;">#accum_cost_svc#</td>
<td bgcolor="##eee" colspan="99" style="border-bottom:2px solid gray;"></td>
</tr >
</cfoutput>
<tr><td colspan="99">&nbsp;</td></tr>
</cfoutput>
</cfoutput>
</table>
<!--- <cfdump var=#report.qSpecification#/> --->
<h3>Спецификация</h3>
<table class="worktable">
<tr>
<th width="5%">Договор</th>
<th width="15%">Доп. соглашение</th>
<th width="3%">Тип оплаты</th>
<th width="20%">Пользовательское наименование</th>
<th width="3%">Кол-во по строке</th>
<th width="3%">Скидка по строке</th>
<th width="5%">НОУ</th>
<th width="5%">КОУ</th>
<th width="10%">Компонент</th>
<th width="5%">Артикул</th>
<th width="5%">Кол-во</th>
<th width="5%">Ед.изм.</th>
<th width="5%">Цена с НДС</th>
<th width="5%">Скидка %</th>
<th width="5%">Цена со скидкой с НДС</th>
</tr>
<cfoutput query="report.qSpecification">
<tr>
<td>#contractOutsideId#</td>
<td>#additionalAgreementName#</td>
<td class="c">#serviceType#</td>
<td>#serviceCustomName#</td>
<td class="c">#serviceQuantity#</td>
<td class="r">#serviceDiscount#</td>
<td class="c">#dateFormat(serviceStart, "YYYY-MM-DD")#</td>
<td class="c">#dateFormat(serviceEnd, "YYYY-MM-DD")#</td>
<td>#component#</td>
<td>#componentCode#</td>
<td class="r">#quantity#</td>
<td class="c">#measureShort#</td>
<td class="r">#gaussianRound(priceWVat,2)#</td>
<td class="r">#discount#</td>
<td class="r">#gaussianRound((100-serviceDiscount)/100*(100-discount)/100*priceWVat,4)#</td>
</tr>
</cfoutput>
</table>
<!---<cfdump var=#report.qCharge#/> --->
<!--- <cfdump var=#this.datasources[request.ds].connectionString#/> --->
<layout:page section="footer"/>
<!--- select * from s3billing.billing_per_user_fix_pl('2025-05-01 00:00:00','2025-06-01 00:00:00','1395'); --->
<cfscript>
function gaussianRound(numeric number, numeric precision = 0) {
var factor = 10 ^ precision;
var scaledNumber = number * factor;
var absScaled = abs(scaledNumber);
var sign = sgn(scaledNumber);
var floored = int(absScaled);
// If it's not ending in exactly .5, use standard rounding or precision scaling
if (absScaled - floored != 0.5) {
return round(number * factor) / factor;
}
// If it is exactly .5, round towards the nearest even number
if (floored % 2 != 0) {
// Nearest even is UP
return (floored + 1) * sign / factor;
} else {
// Nearest even is DOWN
return floored * sign / factor;
}
}
</cfscript>