Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(portal): 提交作业和提交交互式应用时显示可用分区 #977

Merged
merged 14 commits into from
Jun 19, 2024
Merged
8 changes: 8 additions & 0 deletions .changeset/four-timers-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@scow/portal-server": patch
"@scow/mis-server": patch
"@scow/portal-web": patch
"@scow/mis-web": patch
---

修改门户系统下提交作业或交互式应用时可以选择的账号为用户维度未封锁账号,分区为该用户在该集群下对应账号的可用分区;修改从模板提交作业时模板值可以直接提交
5 changes: 5 additions & 0 deletions .changeset/wise-boats-confess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@scow/grpc-api": minor
---

在 common/config 下新增 getAvailablePartitionsForCluster 作为门户/管理系统共用 proto,管理系统下原 proto 标记为已过时;在 listAccounts 下新增可选 AccountStatusFilter 查询参数
21 changes: 20 additions & 1 deletion apps/mis-server/src/services/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ import { status } from "@grpc/grpc-js";
import { getClusterConfigs } from "@scow/config/build/cluster";
import { convertClusterConfigsToServerProtoType, NO_CLUSTERS } from "@scow/lib-server";
import { scowErrorMetadata } from "@scow/lib-server/build/error";
import { libCheckActivatedClusters } from "@scow/lib-server/build/misCommon/clustersActivation";
import { ConfigServiceServer, ConfigServiceService } from "@scow/protos/build/common/config";
import { updateCluster } from "src/bl/clustersUtils";
import { getActivatedClusters, updateCluster } from "src/bl/clustersUtils";

export const configServiceServer = plugin((server) => {
server.addService<ConfigServiceServer>(ConfigServiceService, {
Expand All @@ -36,6 +37,24 @@ export const configServiceServer = plugin((server) => {
return [reply];
},

getAvailablePartitionsForCluster: async ({ request, em, logger }) => {

const { cluster, accountName, userId } = request;

// check cluster activation
const currentActivatedClusters = await getActivatedClusters(em, logger);
libCheckActivatedClusters({ clusterIds: cluster, activatedClusters: currentActivatedClusters, logger });

const reply = await server.ext.clusters.callOnOne(
cluster,
logger,
async (client) => await asyncClientCall(client.config, "getAvailablePartitions", {
accountName, userId,
}),
);
return [reply];

},

getClusterConfigFiles: async ({ em, logger }) => {

Expand Down
6 changes: 6 additions & 0 deletions apps/mis-server/src/services/misConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import { ClusterRuntimeInfo_LastActivationOperation,
import { getActivatedClusters, getClustersRuntimeInfo } from "src/bl/clustersUtils";
import { Cluster, ClusterActivationStatus } from "src/entities/Cluster";

/**
* @deprecated Use the new API function GetAvailablePartitionsForCluster From configServiceServer instead.
*/
export const misConfigServiceServer = plugin((server) => {
server.addService<ConfigServiceServer>(ConfigServiceService, {

Expand Down Expand Up @@ -47,6 +50,9 @@ export const misConfigServiceServer = plugin((server) => {
},


/**
* @deprecated Use the new API function GetAvailablePartitionsForCluster from ./config/configServiceServer instead.
*/
getAvailablePartitionsForCluster: async ({ request, logger }) => {

const { cluster, accountName, userId } = request;
Expand Down
4 changes: 2 additions & 2 deletions apps/mis-web/src/pages/api/job/getAvailableBillingTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import { typeboxRouteSchema } from "@ddadaal/next-typed-api-routes-runtime";
import { asyncClientCall } from "@ddadaal/tsgrpc-client";
import { ConfigServiceClient as MisConfigServerClient } from "@scow/protos/build/server/config";
import { ConfigServiceClient } from "@scow/protos/build/server/config";
import { JobBillingItem } from "@scow/protos/build/server/job";
import { UserStatus } from "@scow/protos/build/server/user";
import { Static, Type } from "@sinclair/typebox";
Expand Down Expand Up @@ -80,7 +80,7 @@ export const GetAvailableBillingTableSchema = typeboxRouteSchema({
export async function getAvailablePartitionForItems(
cluster: string, userId: string, tenantName: string): Promise<Partition[]> {

const client = getClient(MisConfigServerClient);
const client = getClient(ConfigServiceClient);

const statuses = await getUserStatus(userId, tenantName);

Expand Down
27 changes: 26 additions & 1 deletion apps/portal-server/src/services/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { status } from "@grpc/grpc-js";
import { getClusterConfigs } from "@scow/config/build/cluster";
import { checkSchedulerApiVersion, convertClusterConfigsToServerProtoType, NO_CLUSTERS } from "@scow/lib-server";
import { scowErrorMetadata } from "@scow/lib-server/build/error";
import { ConfigServiceServer, ConfigServiceService } from "@scow/protos/build/common/config";
import { ConfigServiceServer, ConfigServiceService, Partition } from "@scow/protos/build/common/config";
import { ConfigServiceServer as runTimeConfigServiceServer, ConfigServiceService as runTimeConfigServiceService }
from "@scow/protos/build/portal/config";
import { ApiVersion } from "@scow/utils/build/version";
Expand All @@ -39,6 +39,31 @@ export const staticConfigServiceServer = plugin((server) => {
return [reply];
},

getAvailablePartitionsForCluster: async ({ request, logger }) => {

const { cluster, accountName, userId } = request;
let availablePartitions: Partition[];

await checkActivatedClusters({ clusterIds: cluster });

try {
const resp = await callOnOne(
cluster,
logger,
async (client) => await asyncClientCall(client.config, "getAvailablePartitions", {
accountName, userId,
}),
);
availablePartitions = resp.partitions;
} catch (error) {
logger.error(`Error occured when query the available partitions of ${userId} in ${accountName}.`);
availablePartitions = [];
}

return [ { partitions: availablePartitions } ];
},


getClusterConfigFiles: async ({ logger }) => {

const clusterConfigs = getClusterConfigs(undefined, logger, ["hpc"]);
Expand Down
63 changes: 59 additions & 4 deletions apps/portal-server/src/services/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { Status } from "@grpc/grpc-js/build/src/constants";
import { jobInfoToPortalJobInfo, jobInfoToRunningjob } from "@scow/lib-scheduler-adapter";
import { checkSchedulerApiVersion } from "@scow/lib-server";
import { createDirectoriesRecursively, sftpReadFile, sftpStat, sftpWriteFile } from "@scow/lib-ssh";
import { JobServiceServer, JobServiceService } from "@scow/protos/build/portal/job";
import { AccountStatusFilter, JobServiceServer, JobServiceService } from "@scow/protos/build/portal/job";
import { parseErrorDetails } from "@scow/rich-error-model";
import { ApiVersion } from "@scow/utils/build/version";
import path, { join } from "path";
Expand Down Expand Up @@ -49,7 +49,7 @@ export const jobServiceServer = plugin((server) => {
},

listAccounts: async ({ request, logger }) => {
const { cluster, userId } = request;
const { cluster, userId, statusFilter } = request;
await checkActivatedClusters({ clusterIds: cluster });

const reply = await callOnOne(
Expand All @@ -60,7 +60,63 @@ export const jobServiceServer = plugin((server) => {
}),
);

return [{ accounts: reply.accounts }];
const accounts = reply.accounts;

if ((statusFilter === undefined) || statusFilter === AccountStatusFilter.ALL) {
return [{ accounts: accounts }];
}

const filteredUnblockedAccounts: string[] = [];
const filteredBlockedAccounts: string[] = [];
const filteredUnblockedUserAccounts: string[] = [];
const filteredBlockedUserAccounts: string[] = [];

const filterAccountPromise = Promise.allSettled(accounts.map(async (account) => {
try {
const resp = await callOnOne(
cluster,
logger,
async (client) => await asyncClientCall(client.account, "queryAccountBlockStatus", {
accountName: account,
}),
);
if (resp.blocked) {
filteredBlockedAccounts.push(account);
} else {
filteredUnblockedAccounts.push(account);
}
} catch (error) {
logger.error(`Error occured when query the block status of ${account}.`, error);
}
}));

const filterUserStatusPromise = Promise.allSettled(accounts.map(async (account) => {
try {
const resp = await callOnOne(
cluster,
logger,
async (client) => await asyncClientCall(client.user, "queryUserInAccountBlockStatus", {
accountName: account, userId,
}),
);
if (resp.blocked) {
filteredBlockedUserAccounts.push(account);
} else {
filteredUnblockedUserAccounts.push(account);
}
} catch (error) {
logger.error(`Error occured when query the block status of ${userId} in ${account}.`, error);
}
}));

await Promise.allSettled([filterAccountPromise, filterUserStatusPromise]);

const unblockAccounts =
filteredUnblockedAccounts.filter((account) => filteredUnblockedUserAccounts.includes(account));
const blockedAccounts = Array.from(new Set(filteredBlockedAccounts.concat(filteredBlockedUserAccounts)));

return [{ accounts:
statusFilter === AccountStatusFilter.BLOCKED_ONLY ? blockedAccounts : unblockAccounts }];
},

getJobTemplate: async ({ request, logger }) => {
Expand Down Expand Up @@ -324,7 +380,6 @@ export const jobServiceServer = plugin((server) => {
return [{ jobId: reply.jobId }];
},


});

});
3 changes: 3 additions & 0 deletions apps/portal-web/src/apis/api.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ export const mockApi: MockApi<typeof api> = {
terminateFileTransfer: null,
checkTransferKey: null,

getAvailablePartitionsForCluster: async () => ({ partitions: []}),
getClusterConfigFiles: async () => ({ clusterConfigs: {
hpc01: {
displayName: "hpc01Name",
Expand All @@ -300,3 +301,5 @@ export const mockApi: MockApi<typeof api> = {

};



2 changes: 2 additions & 0 deletions apps/portal-web/src/apis/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import type { CancelJobSchema } from "src/pages/api/job/cancelJob";
import type { DeleteJobTemplateSchema } from "src/pages/api/job/deleteJobTemplate";
import type { GetAccountsSchema } from "src/pages/api/job/getAccounts";
import type { GetAllJobsSchema } from "src/pages/api/job/getAllJobs";
import type { GetAvailablePartitionsForClusterSchema } from "src/pages/api/job/getAvailablePartitionsForCluster";
import type { GetJobTemplateSchema } from "src/pages/api/job/getJobTemplate";
import type { GetRunningJobsSchema } from "src/pages/api/job/getRunningJobs";
import type { ListJobTemplatesSchema } from "src/pages/api/job/listJobTemplates";
Expand Down Expand Up @@ -108,6 +109,7 @@ export const api = {
deleteJobTemplate: apiClient.fromTypeboxRoute<typeof DeleteJobTemplateSchema>("DELETE", "/api/job/deleteJobTemplate"),
getAccounts: apiClient.fromTypeboxRoute<typeof GetAccountsSchema>("GET", "/api/job/getAccounts"),
getAllJobs: apiClient.fromTypeboxRoute<typeof GetAllJobsSchema>("GET", "/api/job/getAllJobs"),
getAvailablePartitionsForCluster: apiClient.fromTypeboxRoute<typeof GetAvailablePartitionsForClusterSchema>("GET", "/api/job/getAvailablePartitionsForCluster"),
getJobTemplate: apiClient.fromTypeboxRoute<typeof GetJobTemplateSchema>("GET", "/api/job/getJobTemplate"),
getRunningJobs: apiClient.fromTypeboxRoute<typeof GetRunningJobsSchema>("GET", "/api/job/getRunningJobs"),
listJobTemplates: apiClient.fromTypeboxRoute<typeof ListJobTemplatesSchema>("GET", "/api/job/listJobTemplates"),
Expand Down
4 changes: 4 additions & 0 deletions apps/portal-web/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ export default {
selectAccountPlaceholder: "Select Account",
refreshAccountList: "Refresh Account List",
},
partitionSelector: {
selectPartitionPlaceholder: "Select Partition",
refreshPartitionList: "Refresh Partition List",
},
allJobsTable: {
searchForm: {
clusterLabel: "Cluster",
Expand Down
6 changes: 5 additions & 1 deletion apps/portal-web/src/i18n/zh_cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ export default {
selectAccountPlaceholder: "请选择账户",
refreshAccountList: "刷新账户列表",
},
partitionSelector: {
selectPartitionPlaceholder: "请选择分区",
refreshPartitionList: "刷新分区列表",
},
allJobsTable: {
searchForm: {
clusterLabel: "集群",
Expand Down Expand Up @@ -185,7 +189,7 @@ export default {

wdTooltip2: "2. 填写目录不可访问或者不可操作时,提交作业或者作业运行将失败;",

wdTooltip3: "2.该文件用于保存适配器返回的脚本,默认值参考输出文件。",
wdTooltip3: "该文件用于保存适配器返回的脚本,默认值参考输出文件。",

output: "标准输出文件",
errorOutput: "错误输出文件",
Expand Down
5 changes: 5 additions & 0 deletions apps/portal-web/src/models/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,9 @@ export function formatTime(milliseconds: number) {
return text;
}

export enum AccountStatusFilter {
ALL = 0,
BLOCKED_ONLY = 1,
UNBLOCKED_ONLY = 2,
};