Devin.KR

용량 경보 계산

100분 안팎

학습 목표

표준 입력의 전체·사용 블록과 inode 수로 임계값 이상 여부를 계산하며 0·경계값·잘못된 입력을 처리합니다.

개념

경보는 판정 계약부터 고정합니다

운영 점검기는 관측을 받았을 때 담당자가 대응할 수 있는 상태를 반환해야 합니다. 계산식만 맞고 입력 오류를 정상으로 보고하면 가장 필요한 시점에 경보가 사라집니다. 이 레슨은 표준 입력으로 전체 블록·사용 블록·전체 inode·사용 inode·임계값을 순서대로 받습니다. 파일시스템을 직접 조회하지 않는 계산 과제이므로 브라우저에서도 재현할 수 있습니다. 관측 수집과 계산을 분리하면 같은 장애 입력을 반복 검사할 수 있습니다.

입력은 공백으로 구분한 정수 다섯 개입니다. 예를 들어 100 80 10 7 80은 블록 80%, inode 70%, 경보 임계값 80%를 뜻합니다. 임계값 이상이면 경보를 내므로 이 입력은 BLOCK입니다. 출력은 OK, BLOCK, INODE, BOTH, UNKNOWN, INVALID 중 하나를 한 줄로 씁니다. 설명 문장이나 디버깅 숫자를 함께 출력하지 않습니다. 호출자는 이 상태 문자열로 다음 동작을 결정합니다.

유효성·미관측·경보를 서로 구분합니다

먼저 토큰 수와 정수 변환을 확인합니다. 개수가 다르거나 변환할 수 없으면 INVALID입니다. 전체·사용 값은 음수가 아니어야 하고 사용은 전체보다 클 수 없습니다. 임계값은 1부터 100까지 허용합니다. 범위 검사에서 실패한 입력은 계산 전에 끝냅니다. 사용 101, 전체 100이라는 불가능한 관측을 101% 경보로 처리하면 수집기의 오류가 저장 부족처럼 보일 수 있습니다.

범위가 유효하더라도 전체 블록이나 전체 inode가 0이면 UNKNOWN입니다. 이는 사용률의 분모를 결정할 수 없다는 뜻이며 OK와 다릅니다. 전체 0·사용 0을 빈 저장소라고 보고 정상 처리하지 않습니다. 전체 0·사용 1은 사용이 전체를 초과하므로 UNKNOWN 전에 INVALID가 됩니다. 이 검사 순서를 테스트로 고정하면 담당자마다 같은 입력을 다르게 해석하는 문제를 줄입니다.

하나의 축만 관측할 수 있고 다른 축의 전체가 0인 경우에도 이번 계약은 UNKNOWN을 반환합니다. 알려진 축에 경보가 있더라도 전체 저장 상태를 확정할 수 없다는 정책입니다. 실제 점검 제품에서는 축별 상태를 별도로 반환하는 설계도 가능하지만 한 줄 출력 과제에서는 이 계약을 따릅니다. UNKNOWN을 경보가 없다는 뜻으로 대시보드에 표시하지 않고 수집 확인이 필요한 상태로 다룹니다.

반올림보다 정수 비교를 사용합니다

사용률은 사용 나누기 전체 곱하기 100으로 표현할 수 있습니다. 그러나 경보 판정은 사람이 보는 반올림 문자열과 분리합니다. 전체 3·사용 2의 비율은 약 66.67%이며 임계값 67에 아직 도달하지 않았습니다. 화면에서 67%로 반올림해 보여 준 값을 다시 판정 입력으로 쓰면 잘못된 경보가 생깁니다. 원래 정수 관측값을 저장하고 같은 기준으로 비교합니다.

양수인 전체에 대해 used * 100과 total * threshold를 비교하면 나눗셈 없이 정확히 임계값 도달 여부를 결정할 수 있습니다. 블록과 inode에 각각 이 비교를 수행합니다. Python 정수는 이 과제에서 큰 곱셈도 처리하므로 부동소수점 정밀도에 의존하지 않습니다. 다른 언어로 옮기면 정수 범위와 곱셈 넘침을 검토해야 하며 Python의 성질을 모든 언어에 그대로 가정하지 않습니다.

경계값에는 아래·정확히 같음·위 세 종류를 준비합니다. 전체 100·사용 79·임계값 80은 경보가 없고 사용 80은 경보입니다. 전체 0 검사는 나눗셈을 수행하기 전에 처리합니다. 부등호 하나가 바뀌면 정상 사례 대부분은 통과해도 경계 입력이 실패하므로 테스트가 구현의 의미를 드러냅니다. 퍼센트를 소수 둘째 자리까지 예쁘게 출력하는 일은 이번 상태 판정의 요구사항이 아닙니다.

두 축의 조합으로 상태를 만듭니다

블록 경보와 inode 경보를 불리언 값으로 계산한 뒤 둘 다 참이면 BOTH를 반환합니다. 블록만 참이면 BLOCK, inode만 참이면 INODE, 둘 다 거짓이면 OK입니다. 블록을 먼저 검사해 즉시 반환하면 둘 다 부족해도 BLOCK으로 끝날 수 있습니다. 먼저 각 축의 판정을 구하고 조합을 마지막에 처리하면 누락을 줄일 수 있습니다. 실제 오류 원인과 경보 상태는 구분해야 합니다.

BLOCK은 블록 사용률이 임계값 이상이라는 뜻이며 이미 쓰기 실패가 발생했다는 단정이 아닙니다. INODE도 임계값 도달을 뜻합니다. 가용 공간·예약 블록·쿼터·권한·읽기 전용 상태는 이번 입력에 없으므로 OK가 실제 쓰기 가능성의 증명은 아닙니다. 미션은 이 함수의 상태와 실제 계정 쓰기·마운트·HTTP 데이터 검사를 함께 제출하도록 요구합니다. 점검기의 적용 범위를 문서로 설명해야 오해를 줄일 수 있습니다.

INVALID와 UNKNOWN은 블록 경보보다 덜 중요한 상태가 아닙니다. 데이터가 잘못 들어오거나 수집이 빠지면 담당자는 수집기·권한·대상 경로를 확인해야 합니다. 같은 서버에서 지난 결과가 OK였다는 이유로 이번 미관측도 OK로 바꾸지 않습니다. 이 과제는 표준 출력 상태를 정하지만 운영 자동화에서는 실패 종료 코드와 알림 전달을 별도로 설계합니다. 출력 문자열과 프로세스 종료 성공을 혼동하지 않습니다.

입출력을 작은 함수와 진입점으로 나눕니다

evaluate 함수는 정수 다섯 개를 받아 상태만 반환하고 표준 입력을 직접 읽지 않습니다. main에서는 sys.stdin.read().split()으로 토큰을 만들고 개수·정수 변환 실패를 처리합니다. 이 구조는 미션 policy.py에서 함수만 가져와 재사용하기 쉽습니다. 함수를 호출할 때 이미 검증된 입력이라고 가정하지 않고 수치 범위를 다시 검사하면 다른 호출자가 범위 밖 값을 전달한 경우도 안전하게 드러납니다.

브라우저 starter에는 정상 상태만 반환하는 자리 표시자가 있습니다. 제공 테스트를 하나씩 읽고 분기를 추가합니다. INVALID의 사례로 음수·사용 초과·잘못된 임계값·숫자 아닌 문자열·토큰 부족을 다룹니다. UNKNOWN은 전체 블록과 전체 inode 각각 0인 입력으로 검사합니다. BOTH와 정확한 임계값 입력을 빠뜨리지 않으면 단일 축 구현이나 부등호 실수를 잡을 수 있습니다.

예외 처리는 예상 입력 변환 오류만 처리합니다. 모든 예외를 잡아서 OK를 출력하면 코드 오류까지 정상 상태로 숨깁니다. ValueError에서 INVALID를 출력하고, 개발 과정의 다른 예외는 원문을 읽어 수정합니다. 출력에 Traceback이 보이면 어느 줄에서 형식 오류나 계산 오류가 생겼는지 먼저 찾습니다. UNKNOWN으로 처리하려던 0 분모에서 ZeroDivisionError가 발생했다면 검사 순서를 바꿉니다.

같은 판정을 다음 점검 단계로 옮깁니다

실습을 마치면 정상·경계·잘못된 입력의 기대 출력을 설명할 수 있어야 합니다. 운영 환경에서는 수집한 원문·시각·대상 경로를 보존하고 이 함수에 같은 단위의 전체·사용 수치를 전달합니다. 단위가 다른 숫자를 섞으면 수식이 맞아도 의미가 틀립니다. 분기와 반복의 문법을 더 연습할 필요가 있다면 더 읽기의 Python 제어 흐름 원고로 이동하며 여기서는 저장 경보의 판단 계약을 완성합니다.

따라하기

반올림 없는 경계 비교

전체 3·사용 2에서 두 임계값을 직접 비교합니다.

total, used = 3, 2
for threshold in (66, 67):
    print(threshold, used * 100 >= total * threshold)

실행 결과

66 True
67 False

0 분모를 계산 전에 분리

0 전체의 유효한 관측을 UNKNOWN으로 처리합니다.

total, used = 0, 0
if total == 0:
    print('UNKNOWN')
else:
    print(used * 100 / total)

실행 결과

UNKNOWN

한 축과 두 축의 상태 읽기

정상·경계·BOTH를 고정 입력으로 확인합니다.

for tb, ub, ti, ui, threshold in [(100,79,10,7,80),(100,80,10,7,80),(100,80,10,8,80)]:
    b = ub * 100 >= tb * threshold
    i = ui * 100 >= ti * threshold
    print('BOTH' if b and i else 'BLOCK' if b else 'INODE' if i else 'OK')

실행 결과

OK
BLOCK
BOTH

토큰 오류 확인

토큰 수와 변환 오류를 계산 상태와 분리합니다.

for text in ('100 0', 'x 0 10 0 80'):
    tokens = text.split()
    try:
        if len(tokens) != 5: raise ValueError('count')
        values = list(map(int, tokens))
    except ValueError:
        print('INVALID')

실행 결과

INVALID
INVALID

확인 문제

실습

공백 구분 정수 다섯 개: 전체 블록, 사용 블록, 전체 inode, 사용 inode, 임계값(1~100)을 읽습니다. 토큰·변환·음수·사용 초과·임계값 오류는 INVALID, 유효하지만 전체 중 하나가 0이면 UNKNOWN을 먼저 반환합니다. 나머지는 임계값 이상인 축에 따라 BOTH·BLOCK·INODE·OK를 한 줄로 출력합니다. 표시 반올림 없이 정수 교차 곱으로 판정합니다.

모범 답안
import sys

def evaluate(tb, ub, ti, ui, threshold):
    if min(tb, ub, ti, ui) < 0 or ub > tb or ui > ti or not 1 <= threshold <= 100:
        return 'INVALID'
    if tb == 0 or ti == 0:
        return 'UNKNOWN'
    block = ub * 100 >= tb * threshold
    inode = ui * 100 >= ti * threshold
    if block and inode: return 'BOTH'
    if block: return 'BLOCK'
    if inode: return 'INODE'
    return 'OK'

def main():
    tokens = sys.stdin.read().split()
    if len(tokens) != 5:
        print('INVALID'); return
    try: values = [int(token) for token in tokens]
    except ValueError:
        print('INVALID'); return
    print(evaluate(*values))

if __name__ == '__main__': main()

더 읽기

면접 질문

  • 디스크 용량이 남았는데 파일 생성이 실패한 원인을 설명합니다.