Example #1
0
        public async Task <IActionResult> Create([FromBody] CreateInputModel model, [FromServices] INodeRepository nodeRepository)
        {
            //データの入力チェック
            if (!ModelState.IsValid)
            {
                return(JsonBadRequest("Invalid inputs."));
            }
            //データの存在チェック
            var dataSet = await dataSetRepository.GetByIdAsync(model.DataSetId.Value);

            if (dataSet == null)
            {
                return(JsonNotFound($"DataSet ID {model.DataSetId} is not found."));
            }
            if (model.ParentId.HasValue)
            {
                var parent = await trainingHistoryRepository.GetByIdAsync(model.ParentId.Value);

                if (parent == null)
                {
                    return(JsonNotFound($"Training ID {model.ParentId.Value} is not found."));
                }
            }
            if (string.IsNullOrEmpty(model.Partition) == false)
            {
                bool existPartition = await nodeRepository.IsEnablePartitionAsync(model.Partition, true);

                if (existPartition == false)
                {
                    return(JsonNotFound($"There are no enable nodes with Partition {model.Partition}."));
                }
            }

            //同じ名前のコンテナは実行できないので、確認する
            var currentStatus = await clusterManagementLogic.GetContainerStatusAsync(model.Name, CurrentUserInfo.SelectedTenant.Name, false);

            if (currentStatus.Exist())
            {
                if (currentStatus.IsError())
                {
                    return(JsonConflict($"Failed to check cluster status. Please contact your server administrator."));
                }
                return(JsonConflict($"Container {model.Name} already exists: status {currentStatus}"));
            }

            long?  gitId    = model.GitModel.GitId ?? CurrentUserInfo.SelectedTenant.DefaultGit?.Id;
            string branch   = model.GitModel.Branch ?? "master";
            string commitId = model.GitModel.CommitId;

            //コミットIDが指定されていなければ、ブランチのHEADからコミットIDを取得する
            if (string.IsNullOrEmpty(commitId))
            {
                commitId = await gitLogic.GetCommitIdAsync(gitId.Value, model.GitModel.Repository, model.GitModel.Owner, branch);

                if (string.IsNullOrEmpty(commitId))
                {
                    //コミットIDが特定できなかったらエラー
                    return(JsonNotFound($"The branch {branch} for {gitId.Value}/{model.GitModel.Owner}/{model.GitModel.Repository} is not found."));
                }
            }

            //コンテナの実行前に、推論履歴を作成する(コンテナの実行に失敗した場合、そのステータスをユーザに表示するため)
            var inferenceHistory = new InferenceHistory()
            {
                Name                 = model.Name,
                DisplayId            = -1,
                ContainerRegistryId  = model.ContainerImage.RegistryId ?? CurrentUserInfo.SelectedTenant.DefaultRegistry?.Id,
                ContainerImage       = model.ContainerImage.Image,
                ContainerTag         = model.ContainerImage.Tag, //latestは運用上使用されていないハズなので、そのまま直接代入
                DataSetId            = model.DataSetId.Value,
                EntryPoint           = model.EntryPoint,
                ModelGitId           = gitId,
                ModelRepository      = model.GitModel.Repository,
                ModelRepositoryOwner = model.GitModel.Owner,
                ModelBranch          = branch,
                ModelCommitId        = commitId,
                OptionDic            = model.Options ?? new Dictionary <string, string>(), //オプションはnullの可能性があるので、その時は初期化
                ParentId             = model.ParentId,
                Memo                 = model.Memo,
                Cpu       = model.Cpu.Value,
                Memory    = model.Memory.Value,
                Gpu       = model.Gpu.Value,
                Partition = model.Partition,
                Status    = ContainerStatus.Running.Key
            };

            if (inferenceHistory.OptionDic.ContainsKey("")) //空文字は除外する
            {
                inferenceHistory.OptionDic.Remove("");
            }

            inferenceHistoryRepository.Add(inferenceHistory);
            if (dataSet.IsLocked == false)
            {
                dataSet.IsLocked = true;
            }
            unitOfWork.Commit();


            var result = await clusterManagementLogic.RunInferenceContainerAsync(inferenceHistory);

            if (result.IsSuccess == false)
            {
                //コンテナの起動に失敗した状態。エラーを出力して、保存した推論履歴も削除する。
                inferenceHistoryRepository.Delete(inferenceHistory);
                unitOfWork.Commit();

                return(JsonError(HttpStatusCode.ServiceUnavailable, "Failed to run training. The message bellow may be help to resolve: " + result.Error));
            }

            //結果に従い、推論結果を更新する。
            //実行には時間がかかりうるので、DBから最新の情報を取ってくる
            inferenceHistory = await inferenceHistoryRepository.GetByIdAsync(inferenceHistory.Id);

            inferenceHistory.Configuration = result.Value.Configuration;
            inferenceHistory.Status        = result.Value.Status.Key;
            unitOfWork.Commit();

            if (result.Value.Status.Succeed())
            {
                return(JsonCreated(new InferenceSimpleOutputModel(inferenceHistory)));
            }
            else
            {
                return(JsonError(HttpStatusCode.ServiceUnavailable, $"Failed to run training. Status={result.Value.Status.Name}. Please contact your server administrator."));
            }
        }
        public async Task <IActionResult> Create([FromBody] CreateInputModel model)
        {
            //データの入力チェック
            if (!ModelState.IsValid)
            {
                return(JsonBadRequest("Invalid inputs."));
            }
            //データの存在チェック
            var dataSet = await dataSetRepository.GetByIdAsync(model.DataSetId.Value);

            if (dataSet == null)
            {
                return(JsonNotFound($"DataSet ID {model.DataSetId} is not found."));
            }
            if (string.IsNullOrEmpty(model.Partition) == false)
            {
                bool existPartition = await nodeRepository.IsEnablePartitionAsync(model.Partition, true);

                if (existPartition == false)
                {
                    return(JsonNotFound($"There are no enable nodes with Partition {model.Partition}."));
                }
            }

            // 環境変数名のチェック
            if (model.Options != null && model.Options.Count > 0)
            {
                foreach (var env in model.Options)
                {
                    if (!string.IsNullOrEmpty(env.Key))
                    {
                        // フォーマットチェック
                        if (!Regex.IsMatch(env.Key, "^[-._a-zA-Z][-._a-zA-Z0-9]*$"))
                        {
                            return(JsonNotFound($"Invalid envName. Please match the format of '^[-._a-zA-Z][-._a-zA-Z0-9]*$'."));
                        }
                    }
                }
            }

            long?  gitId    = model.GitModel.GitId ?? CurrentUserInfo.SelectedTenant.DefaultGit?.Id;
            string branch   = model.GitModel.Branch ?? "master";
            string commitId = model.GitModel.CommitId;

            //コミットIDが指定されていなければ、ブランチのHEADからコミットIDを取得する
            if (string.IsNullOrEmpty(commitId))
            {
                commitId = await gitLogic.GetCommitIdAsync(gitId.Value, model.GitModel.Repository, model.GitModel.Owner, branch);

                if (string.IsNullOrEmpty(commitId))
                {
                    //コミットIDが特定できなかったらエラー
                    return(JsonNotFound($"The branch {branch} for {gitId.Value}/{model.GitModel.Owner}/{model.GitModel.Repository} is not found."));
                }
            }

            // 各リソースの超過チェック
            Tenant tenant       = tenantRepository.Get(CurrentUserInfo.SelectedTenant.Id);
            string errorMessage = clusterManagementLogic.CheckQuota(tenant, model.Cpu.Value, model.Memory.Value, model.Gpu.Value);

            if (errorMessage != null)
            {
                return(JsonBadRequest(errorMessage));
            }

            //コンテナの実行前に、推論履歴を作成する(コンテナの実行に失敗した場合、そのステータスをユーザに表示するため)
            var inferenceHistory = new InferenceHistory()
            {
                Name                 = model.Name,
                DisplayId            = -1,
                ContainerRegistryId  = model.ContainerImage.RegistryId ?? CurrentUserInfo.SelectedTenant.DefaultRegistry?.Id,
                ContainerImage       = model.ContainerImage.Image,
                ContainerTag         = model.ContainerImage.Tag, //latestは運用上使用されていないハズなので、そのまま直接代入
                DataSetId            = model.DataSetId.Value,
                EntryPoint           = model.EntryPoint,
                ModelGitId           = gitId,
                ModelRepository      = model.GitModel.Repository,
                ModelRepositoryOwner = model.GitModel.Owner,
                ModelBranch          = branch,
                ModelCommitId        = commitId,
                OptionDic            = model.Options ?? new Dictionary <string, string>(), //オプションはnullの可能性があるので、その時は初期化
                Memo                 = model.Memo,
                Cpu          = model.Cpu.Value,
                Memory       = model.Memory.Value,
                Gpu          = model.Gpu.Value,
                Partition    = model.Partition,
                Status       = ContainerStatus.Running.Key,
                Zip          = model.Zip,
                LocalDataSet = model.LocalDataSet,
            };

            if (inferenceHistory.OptionDic.ContainsKey("")) //空文字は除外する
            {
                inferenceHistory.OptionDic.Remove("");
            }
            // 親学習が指定されていれば存在チェック
            if (model.ParentIds != null)
            {
                var maps = new List <InferenceHistoryParentMap>();

                foreach (var parentId in model.ParentIds)
                {
                    var parent = await trainingHistoryRepository.GetByIdAsync(parentId);

                    if (parent == null)
                    {
                        return(JsonNotFound($"Training ID {parentId} is not found."));
                    }
                    // 推論履歴に親学習を紐づける
                    var map = inferenceHistoryRepository.AttachParentAsync(inferenceHistory, parent);
                    if (map != null)
                    {
                        maps.Add(map);
                    }
                }

                inferenceHistory.ParentMaps = maps;
            }


            // 親推論が指定されていれば存在チェック
            if (model.InferenceIds != null)
            {
                var maps = new List <InferenceHistoryParentInferenceMap>();

                foreach (var parentId in model.InferenceIds)
                {
                    var parentInference = await inferenceHistoryRepository.GetByIdAsync(parentId);

                    if (parentInference == null)
                    {
                        return(JsonNotFound($"Inference ID {parentId} is not found."));
                    }
                    // 推論履歴に親推論を紐づける
                    var map = inferenceHistoryRepository.AttachParentInferenceToInferenceAsync(inferenceHistory, parentInference);
                    if (map != null)
                    {
                        maps.Add(map);
                    }
                }
                inferenceHistory.ParentInferenceMaps = maps;
            }

            inferenceHistoryRepository.Add(inferenceHistory);
            if (dataSet.IsLocked == false)
            {
                dataSet.IsLocked = true;
            }
            unitOfWork.Commit();


            var result = await clusterManagementLogic.RunInferenceContainerAsync(inferenceHistory);

            if (result.IsSuccess == false)
            {
                //コンテナの起動に失敗した状態。エラーを出力して、保存した推論履歴も削除する。
                await dataSetLogic.ReleaseLockAsync(inferenceHistory.DataSetId);

                inferenceHistoryRepository.Delete(inferenceHistory);
                unitOfWork.Commit();

                return(JsonError(HttpStatusCode.ServiceUnavailable, "Failed to run training. The message bellow may be help to resolve: " + result.Error));
            }

            //結果に従い、推論結果を更新する。
            //実行には時間がかかりうるので、DBから最新の情報を取ってくる
            inferenceHistory = await inferenceHistoryRepository.GetByIdAsync(inferenceHistory.Id);

            inferenceHistory.Configuration = result.Value.Configuration;
            inferenceHistory.Status        = result.Value.Status.Key;
            unitOfWork.Commit();

            if (result.Value.Status.Succeed())
            {
                return(JsonCreated(new InferenceSimpleOutputModel(inferenceHistory)));
            }
            else
            {
                return(JsonError(HttpStatusCode.ServiceUnavailable, $"Failed to run training. Status={result.Value.Status.Name}. Please contact your server administrator."));
            }
        }