Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions dummy_data.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
-- Dummy data for testing
-- Execute in order: resource_groups → nodes → gpus, groups, container_images

-- 1. Resource Groups
INSERT INTO resource_groups (rsgroup_id, resource_group_name, description, server_name) VALUES
(1, 'RTX 4090 Cluster', 'High-performance GPU cluster with RTX 4090 cards', 'LAB'),
(2, 'RTX 3090 Ti Cluster', 'Mid-range GPU cluster with RTX 3090 Ti cards', 'FARM'),
(3, 'A100 Server', 'Enterprise GPU server with A100 cards', 'LAB'),
(4, 'RTX 3080 Development', 'Development environment with RTX 3080 cards', 'FARM');

-- 2. Nodes
INSERT INTO nodes (node_id, rsgroup_id, memory_size_GB, CPU_core_count) VALUES
('LAB1', 1, 128, 32),
('LAB2', 1, 64, 16),
('LAB3', 3, 256, 64),
('LAB4', 3, 512, 96),
('FARM1', 2, 128, 32),
('FARM2', 2, 256, 64),
('FARM6', 4, 64, 16),
('FARM7', 4, 32, 8),
('FARM8', 2, 128, 32),
('FARM9', 4, 64, 16);

-- 3. GPUs (homogeneous per node)
INSERT INTO gpus (node_id, gpu_model, RAM_GB) VALUES
-- LAB1 (2x RTX 4090)
('LAB1', 'RTX 4090', 24),
('LAB1', 'RTX 4090', 24),
-- LAB2 (1x RTX 4090)
('LAB2', 'RTX 4090', 24),
-- LAB3 (4x A100)
('LAB3', 'A100', 80),
('LAB3', 'A100', 80),
('LAB3', 'A100', 80),
('LAB3', 'A100', 80),
-- LAB4 (8x A100)
('LAB4', 'A100', 80),
('LAB4', 'A100', 80),
('LAB4', 'A100', 80),
('LAB4', 'A100', 80),
('LAB4', 'A100', 80),
('LAB4', 'A100', 80),
('LAB4', 'A100', 80),
('LAB4', 'A100', 80),
-- FARM1 (2x RTX 3090 Ti)
('FARM1', 'RTX 3090 Ti', 24),
('FARM1', 'RTX 3090 Ti', 24),
-- FARM2 (4x RTX 3090 Ti)
('FARM2', 'RTX 3090 Ti', 24),
('FARM2', 'RTX 3090 Ti', 24),
('FARM2', 'RTX 3090 Ti', 24),
('FARM2', 'RTX 3090 Ti', 24),
-- FARM6 (2x RTX 3080)
('FARM6', 'RTX 3080', 10),
('FARM6', 'RTX 3080', 10),
-- FARM7 (1x RTX 3080)
('FARM7', 'RTX 3080', 10),
-- FARM8 (2x RTX 3090 Ti)
('FARM8', 'RTX 3090 Ti', 24),
('FARM8', 'RTX 3090 Ti', 24),
-- FARM9 (2x RTX 3080)
('FARM9', 'RTX 3080', 10),
('FARM9', 'RTX 3080', 10);

-- 4. Used IDs for groups
INSERT INTO used_ids (id_value) VALUES
(1001),
(1002),
(1003),
(1004),
(1005);

-- 5. Groups
INSERT INTO `groups` (ubuntu_gid, group_name) VALUES
(1001, 'ml-researchers'),
(1002, 'data-scientists'),
(1003, 'ai-developers'),
(1004, 'gpu-users'),
(1005, 'admin-team');

-- 6. Container Images (CUDA versions only)
INSERT INTO container_image (image_name, image_version, cuda_version, description, created_at, updated_at) VALUES
('cuda', '11.8', '11.8', 'CUDA 11.8 development environment', NOW(), NOW()),
('cuda', '12.0', '12.0', 'CUDA 12.0 development environment', NOW(), NOW()),
('cuda', '11.7', '11.7', 'CUDA 11.7 development environment', NOW(), NOW()),
('cuda', '12.1', '12.1', 'CUDA 12.1 development environment', NOW(), NOW()),
('cuda', '11.6', '11.6', 'CUDA 11.6 development environment', NOW(), NOW()),
('cuda', '12.2', '12.2', 'CUDA 12.2 development environment', NOW(), NOW()),
('cuda', '11.5', '11.5', 'CUDA 11.5 development environment', NOW(), NOW()),
('cuda', '12.3', '12.3', 'CUDA 12.3 development environment', NOW(), NOW());
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ public class DashboardController implements DashBoardApi {
* GET /api/dashboard/me/servers
*
* @param principal 현재 로그인한 사용자의 인증 정보 (CustomUserDetails)
* @param status 조회할 서버 요청의 상태 (필수 값: PENDING, FULFILLED, DENIED)
* @param status 조회할 서버 요청의 상태 (필수 값: PENDING, FULFILLED, DENIED, ALL)
* 사용자의 승인받은 서버 목록 또는 승인 대기중인 신청 목록을 필터링하여 반환합니다.
* 'ALL' 상태를 사용하면 모든 상태의 요청을 반환합니다.
*/
@GetMapping("/me/servers")
public ResponseEntity<SuccessResponse<?>> getUserServers(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package DGU_AI_LAB.admin_be.domain.dashboard.service;

import DGU_AI_LAB.admin_be.domain.containerImage.dto.response.ContainerImageResponseDTO;
import DGU_AI_LAB.admin_be.domain.nodes.entity.Node;
import DGU_AI_LAB.admin_be.domain.nodes.repository.NodeRepository;
import DGU_AI_LAB.admin_be.domain.requests.dto.response.UserServerResponseDTO;
Expand Down Expand Up @@ -31,7 +32,12 @@ public class DashboardService {
public List<UserServerResponseDTO> getUserServers(Long userId, Status status) {
log.info("[getUserServers] userId={}의 status={} 서버 목록 조회 시작", userId, status);

List<Request> requests = requestRepository.findByUserUserIdAndStatus(userId, status);
List<Request> requests;
if (status == Status.ALL) {
requests = requestRepository.findAllByUser_UserId(userId);
} else {
requests = requestRepository.findByUserUserIdAndStatus(userId, status);
}

return requests.stream()
.map(request -> {
Expand All @@ -53,12 +59,18 @@ public List<UserServerResponseDTO> getUserServers(Long userId, Status status) {
}
}

ContainerImageResponseDTO containerImageDTO = null;
if (request.getContainerImage() != null) {
containerImageDTO = ContainerImageResponseDTO.fromEntity(request.getContainerImage());
}

return UserServerResponseDTO.fromEntity(
request,
serverAddress,
cpuCoreCount,
memoryGB,
resourceGroupName
resourceGroupName,
containerImageDTO
);
})
.collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package DGU_AI_LAB.admin_be.domain.groups.controller;

import DGU_AI_LAB.admin_be.domain.groups.dto.request.CreateGroupRequestDTO;
import DGU_AI_LAB.admin_be.domain.groups.dto.response.GroupResponseDTO;
import DGU_AI_LAB.admin_be.domain.groups.service.GroupService;
import DGU_AI_LAB.admin_be.global.common.SuccessResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import java.util.List;

Expand All @@ -23,12 +23,22 @@ public class GroupController {
/**
* 모든 그룹 정보 조회 API
* GET /api/groups
*
* 그룹 ID와 그룹명을 반환합니다.
*/
@GetMapping("")
@GetMapping
public ResponseEntity<SuccessResponse<?>> getGroups() {
List<GroupResponseDTO> groups = groupService.getAllGroups();
return SuccessResponse.ok(groups);
}

/**
* 새로운 그룹을 생성하는 API
* POST /api/groups
*/
@PostMapping
public ResponseEntity<SuccessResponse<?>> createGroup(@RequestBody @Valid CreateGroupRequestDTO dto) {
log.info("[createGroup] 새로운 그룹 생성 요청 접수: {}", dto.groupName());
GroupResponseDTO response = groupService.createGroup(dto);
return SuccessResponse.created(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package DGU_AI_LAB.admin_be.domain.groups.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Positive;
import lombok.Builder;

@Builder
public record CreateGroupRequestDTO(
@NotNull @Positive
Long ubuntuGid,

@NotBlank
String groupName
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,21 @@
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@EqualsAndHashCode(of = "ubuntuGid")
@EqualsAndHashCode(of = "groupId")
public class Group {

@Id
@Column(name = "ubuntu_gid", nullable = false)
private Long ubuntuGid;
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "group_id")
private Long groupId;

@Column(name = "group_name", nullable = false, length = 100)
private String groupName;

@Column(name = "ubuntu_gid", unique = true, nullable = false)
private Long ubuntuGid;

@OneToOne(fetch = FetchType.LAZY)
@MapsId
@JoinColumn(name = "ubuntu_gid")
@JoinColumn(name = "ubuntu_gid", referencedColumnName = "id_value", insertable = false, updatable = false)
private UsedId usedId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
@Repository
public interface GroupRepository extends JpaRepository<Group, Long> {
List<Group> findAllByUbuntuGidIn(Set<Long> ubuntuGids);
}
boolean existsByUbuntuGid(Long ubuntuGid);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package DGU_AI_LAB.admin_be.domain.groups.service;

import DGU_AI_LAB.admin_be.domain.groups.dto.request.CreateGroupRequestDTO;
import DGU_AI_LAB.admin_be.domain.groups.dto.response.GroupResponseDTO;
import DGU_AI_LAB.admin_be.domain.groups.entity.Group;
import DGU_AI_LAB.admin_be.domain.groups.repository.GroupRepository;
import DGU_AI_LAB.admin_be.domain.usedIds.entity.UsedId;
import DGU_AI_LAB.admin_be.domain.usedIds.repository.UsedIdRepository;
import DGU_AI_LAB.admin_be.error.ErrorCode;
import DGU_AI_LAB.admin_be.error.exception.BusinessException;
import lombok.RequiredArgsConstructor;
Expand All @@ -10,6 +14,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Slf4j
@Service
Expand All @@ -18,6 +23,7 @@
public class GroupService {

private final GroupRepository groupRepository;
private final UsedIdRepository usedIdRepository;

/**
* 모든 그룹 정보를 조회하는 API
Expand All @@ -39,4 +45,42 @@ public List<GroupResponseDTO> getAllGroups() {
log.info("[getAllGroups] 모든 그룹 정보 조회 완료. {}개 그룹", response.size());
return response;
}
}

/**
* 새로운 그룹을 생성하는 API
* POST /api/groups
*/
@Transactional
public GroupResponseDTO createGroup(CreateGroupRequestDTO dto) {
log.info("[createGroup] 그룹 생성 요청 시작: groupName={}, ubuntuGid={}", dto.groupName(), dto.ubuntuGid());

// 1. 요청받은 GID로 UsedId가 이미 존재하는지 확인하고, 없으면 생성
Optional<UsedId> existingUsedId = usedIdRepository.findById(dto.ubuntuGid());
UsedId usedId;
if (existingUsedId.isPresent()) {
log.warn("[createGroup] 중복된 GID가 UsedId에 이미 존재합니다: {}", dto.ubuntuGid());
usedId = existingUsedId.get();
} else {
usedId = usedIdRepository.saveAndFlush(UsedId.builder().idValue(dto.ubuntuGid()).build());
log.info("[createGroup] UsedId에 GID {} 할당 완료", dto.ubuntuGid());
}

// 2. 해당 GID의 그룹이 이미 존재하는지 확인
if (groupRepository.existsByUbuntuGid(dto.ubuntuGid())) {
log.warn("[createGroup] 중복된 GID를 가진 그룹이 이미 존재합니다: {}", dto.ubuntuGid());
throw new BusinessException(ErrorCode.DUPLICATE_GROUP_ID);
}

// 3. 그룹 엔티티 생성 및 저장
Group group = Group.builder()
.groupName(dto.groupName())
.ubuntuGid(dto.ubuntuGid())
.usedId(usedId)
.build();

group = groupRepository.save(group);
log.info("[createGroup] 그룹 생성 완료: id={}, name={}", group.getGroupId(), group.getGroupName());

return GroupResponseDTO.fromEntity(group);
}
}
Loading