0. 제작동기
회사에 웹서버가 있는데
여기에 배포를 하기도 하고 스케줄러를 켜기도 하는 등
팀원들이 자주 들락날락 하는 곳이다
문제는 누군가가 접속중인지 알 수 없었고
사용중인 누군가가 강제 종료 당하는 일이 일수였다.
그래서 강제 종료당하셔서 빡친 팀장님이나 과장님이
사용할때 좀 물어보고 사용하자고 하셔서
팀원들이 '혹시 웹서버 접속하신분~ DB서버 접속하신분~' 이렇게 물어보기 시작하면서 ㅋㅋㅋㅋ
좀 줄어들긴 했으나 문제는 팀장님 과장님이 안물어보고 하시네,,?
그리고 매번 물어보는것도 골때린다고 생각했다.
그래서 누가 접속중인지를 알려주는 툴을 만들어야겠다 결심했다.
1. 개발환경
빠른 제작을 위해 개발은 python으로 했다.
db는 mariaDB를 사용했고
ui는 tkinter을 사용했다.
배포,업데이트 관련은 git을 사용했다
2. 간단한 로직설명
로직은 단순했다.
접속시
현재 접속한 사람이 있다면 누구인지 알려주고
예를 눌으면 강제접속
아니요는 취소
접속한 사람이 없으면 접속을 바로 진행하는데 접속시 내가 누군지 DB에 기록했고
종료시 DB에 기록하는 방식이다.
3. 상세설명
1) 내정보
내 정보같은경우는 디렉터리내에 user정보를 담은 메모장파일을 하나 두었다
여기서 이름을 읽어 db에 넣어주는 방식이었다.
2) 서버구분
여기에 추가적으로 원격 데스크톱을 사용시
원격접속 주소를 알아야지 접속을 하거나
콤보박스에 기록이 저장되어있다한들
주소만보고 이게 무슨 서버인지 구분을 해야 했어서 좀 불편했다
이를 해결하고자 DB에 서버 정보도 저장하여
서버명만 보고 접속 할 수 있도록 하였다.
3) 툴을 통해 원격데스크톱열기 & 종료체크
툴에서 원격데스크톱을 열기위해
원격데스크톱을 열어주는 mstsc 실행 명령어를 사용했다
종료를 체크하는 기능을 어떻게 구현해야할지가 좀 어려웠는데
원격데스크톱을 실행시
실행된 원격데스크톱의 pid를 저장한다.
그 pid를 반복문으로 계속 검색을 한다.
그럼 실행중일때에는 당연히 실행중이기때문에 해당 pid가 계속 검색되지만
종료된다면 검색이 되지 않을테고,
그때 반복문을 탈출시켜
종료로직을 실행시키게 했다.
import subprocess
import time
from dbterm import termremo
def openremote(hostaddress, serverport, myname, conntype):
try:
server_address = hostaddress
server_port = serverport
# MSTSC 실행 명령어
mstsc_command = f"mstsc /admin /v:{server_address}:{server_port}"
# MSTSC 실행
mstsc_process = subprocess.Popen(mstsc_command, shell=True)
# 원격 데스크톱 연결의 PID 찾기
while True:
try:
# MSTSC 프로세스의 상태 확인
if mstsc_process.poll() is not None:
break
# MSTSC 실행 중인 프로세스의 PID 찾기
result = subprocess.run(
'wmic process where "CommandLine like \'%mstsc%\' and not CommandLine like \'%wmic%\'" get ProcessId /format:value',
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True
)
output = result.stdout.strip()
rdp_pid = int(output.split('=')[1])
except ValueError:
# 정수로 변환할 수 없는 경우 계속 시도
pass
except IndexError:
pass
finally:
print(f"MSTSC 프로세스가 종료되었습니다")
termremo(myname, conntype)
if __name__ == "__main__":
openremote()
4) .py -> exe 변환
라이브러리를 따로 설치 할 필요없 팀원들이 이툴을 바로 사용하려면
exe파일로 변환해야했다.
https://jwinjection.tistory.com/152
여러 모듈이 있는 python 실행파일만들기
하나의 py파일에서 여러 모듈(py)을 import 해서 사용하는 경우 1. pip install cx_Freeze 설치해야함 2. setup.py생성후 내용작성해야함 3. 이후 터미널창에서 python setup.py build 입력하면 exe파일 생성됨 setup.py
jwinjection.tistory.com
파이썬프로젝트를 exe로 변환했다
5) 업데이트파일 만들기
우리회사는 BonoboGit을 사용한다.
그래서 git에 배포파일을 등록한다음
최신 파일로 update해주는 batch파일을 작성했다.
기존꺼 제거하고 깃에서 클론받아서 최신껄로 복붙해주는 로직이다
@echo off
del /f main.exe
del /f python3.dll
del /f python311.dll
rmdir /s /q lib
git clone -b exebranch --single-branch http://git주소.git
cd CHECKSERVER
cd remote
move lib ../../
move main.exe ../../
move python3.dll ../../
move python311.dll ../../
cd ../../
rmdir /s /q CHECKSERVER
4. 전체코드
comp.py
from dbsel import dbsel
from dbconn import conn
from openremote import openremote
import tkinter as tk
from tkinter import messagebox
def comp(myname,serverinfo):
dbtype = serverinfo[0]
dbname = serverinfo[1]
sel = dbsel(dbtype)[0]
conneteduser = sel[1]
servertype = sel[2]
state = sel[3]
hostaddress = serverinfo[2]
serverport = serverinfo[3]
if(state == 'ON'):
msg = ('현재 ' + conneteduser + '님이 ' + dbname + '에 접속중입니다. \n강제접속 하시겠습니까?')
result = messagebox.askquestion("확인", msg)
if result == "yes":
conn(myname,servertype)
openremote(hostaddress,serverport,myname,dbtype)
else:
print(result)
else:
print("접속하겠습니다")
conn(myname,servertype)
openremote(hostaddress,serverport,myname,dbtype)
dbconn.py
import pymysql
from datetime import datetime
from dbinfo import getDbConfig
def conn(myname,servertype):
# MariaDB 연결 정보 설정
db_config = getDbConfig()
# MariaDB 연결
conn = pymysql.connect(**db_config)
cursor = conn.cursor()
# 현재 시간 구하기
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 현재 접속할 사용자 명
name = myname
conn_type = str(servertype)
# 업데이트할 데이터
conn_time = current_time
conn_name = name
conn_state = 'ON'
# SQL 쿼리 작성하여 데이터 업데이트
#update_query = """
# UPDATE remote
# SET conn_time = %s, conn_name = %s, conn_state = %s
# WHERE conn_type = %s;
#"""
#update_query = "UPDATE remote SET conn_time = '"+conn_time+"', conn_name = '"+conn_name+"', conn_state = '"+conn_state+"' WHERE conn_type = '"+conn_type+"'";
update_query = "UPDATE remote SET conn_name = '"+conn_name+"', conn_state = 'ON' WHERE conn_type = '"+conn_type+"'";
# 실제 값으로 쿼리 실행
#cursor.execute(update_query, (conn_time, conn_name, conn_state, conn_type))
cursor.execute(update_query)
# 변경사항을 DB에 반영
conn.commit()
# 연결 종료
cursor.close()
conn.close()
if __name__ == "__main__":
conn()
dbinfo.py
# dbsel.py
def getDbConfig():
# MariaDB 연결 정보 설정
db_config = {
'host': 'db주소',
'port': 포트번호,
'user': '아이디',
'password': '비번',
'db': 'db명',
}
# 결과 반환
return db_config
if __name__ == "__getDbConfig__":
getDbConfig()
dbsel.py
import pymysql
from dbinfo import getDbConfig
# dbsel.py
def dbsel(dbtype):
# MariaDB 연결 정보 설정
db_config = getDbConfig()
# type
type = str(dbtype)
# MariaDB 연결
conn = pymysql.connect(**db_config)
cursor = conn.cursor()
# conn_type이 'gjweb'인 데이터를 선택하는 SQL 쿼리
select_query = "SELECT * FROM remote WHERE conn_type = '"+type+"';"
# 쿼리 실행
cursor.execute(select_query)
# 결과 가져오기
result = cursor.fetchall()
# 연결 종료
cursor.close()
conn.close()
# 결과 반환
return result
if __name__ == "__dbsel__":
dbsel()
dbserversel.py
import pymysql
from dbinfo import getDbConfig
# dbsel.py
def dbserversel():
# MariaDB 연결 정보 설정
db_config = getDbConfig()
# MariaDB 연결
conn = pymysql.connect(**db_config)
cursor = conn.cursor()
# conn_type이 'gjweb'인 데이터를 선택하는 SQL 쿼리
select_query = "SELECT * FROM serverinfo"
# 쿼리 실행
cursor.execute(select_query)
# 결과 가져오기
result = cursor.fetchall()
# 연결 종료
cursor.close()
conn.close()
# 결과 반환
return result
if __name__ == "__dbserversel__":
dbserversel()
dbterm.py
import pymysql
from datetime import datetime
from tkinter import messagebox
from dbinfo import getDbConfig
def show_confirmation_dialog():
messagebox.showinfo("확인", "정상 종료되었습니다.")
def termremo(myname,conntype):
# MariaDB 연결 정보 설정
db_config = getDbConfig()
# MariaDB 연결
conn = pymysql.connect(**db_config)
cursor = conn.cursor()
# 현재 시간 구하기
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
# 현재 종료할 사용자 명
name = myname
conn_type = str(conntype)
# 업데이트할 데이터
term_time = current_time
conn_name = name
conn_state = 'OFF'
# SQL 쿼리 작성하여 데이터 업데이트
#update_query = """
# UPDATE remote
# SET term_time = %s, conn_name = %s, conn_state = %s
# WHERE conn_type = %s;
#"""
#update_query = "UPDATE remote SET conn_time = '"+conn_time+"', conn_name = '"+conn_name+"', conn_state = '"+conn_state+"' WHERE conn_type = '"+conn_type+"'";
update_query = "UPDATE remote SET conn_name = '"+conn_name+"', conn_time = '"+term_time+"', conn_state = 'OFF' WHERE conn_type = '"+conn_type+"' and conn_name = '"+conn_name+"'";
# 실제 값으로 쿼리 실행
#cursor.execute(update_query, (term_time, conn_name, conn_state, conn_type))
cursor.execute(update_query)
# 변경사항을 DB에 반영
conn.commit()
# 연결 종료
cursor.close()
conn.close()
show_confirmation_dialog()
if __name__ == "__main__":
termremo()
getuserinfo.py
def myname():
file_path = 'C:/remote/userinfo.txt'
# 가능한 여러 인코딩 시도
encodings_to_try = ['utf-8', 'cp1252', 'cp949']
for encoding in encodings_to_try:
try:
with open(file_path, 'r', encoding=encoding) as file:
lines = file.readlines()
# 파일이 성공적으로 열렸을 때
break
except UnicodeDecodeError:
print(f"인코딩 '{encoding}'으로 파일을 열 수 없습니다.")
# 이름 추출
for line in lines:
if 'username :' in line:
name = line.split(':')[-1].strip()
print("이름:", name)
return name
if __name__ == "__myname__":
myname()
main.py
from tkinter import Tk, Canvas, ttk,Label, messagebox
from dbserversel import dbserversel
from getuserinfo import myname
from comp import comp
#pip install cx_Freeze 설치해야함
#setup.py생성후 내용작성해야함
#이후 터미널창에서 python setup.py build 입력하면 exe파일 생성됨
lst = dbserversel()
myname =myname()
svlist = [item[1] for item in lst]
def platform():
selected_index = combo.current()
if selected_index >= 0 and selected_index < len(lst):
selected_data = lst[selected_index]
#messagebox.showinfo(title="선택한 서버 정보", message=str(selected_data))
comp(myname,selected_data)
else:
messagebox.showinfo(title="오류", message="올바른 서버를 선택하세요.")
window = Tk()
window.title("Server Selector") # 창 제목 추가
# 위쪽 여백
top_space = Canvas(window, height=10)
top_space.pack()
# 텍스트 레이블
info_label = Label(window, text="내정보 : "+ myname, font=('Arial', 16))
info_label.pack(pady=5) # 여백 추가
# 콤보박스 스타일 설정
style = ttk.Style()
style.configure("TCombobox.field",padding=(5, 5, 5, 5), font=('Arial', 20))
# 콤보박스
combo = ttk.Combobox(window, state="readonly", style="TCombobox", width=20,height=10) # width 옵션으로 폭 조절
combo['values'] = svlist
combo.current(0)
combo.pack(padx=10, pady=20, anchor="n") # 여백 조절 및 위치 조정
# 버튼
action = ttk.Button(window, text="접속하기", command=platform, width=20)
action.pack(side="bottom", pady=20) # 버튼 아래쪽에 배치
# 버튼의 크기 조절 (padding 옵션 활용)
style.configure("TButton", padding=(10, 5, 10, 5))
# 가운데 정렬
combo.pack(side="top")
action.pack(side="top")
# 창 크기 조절
window.geometry("250x200") # 필요한 크기로 변경
window.mainloop()
openremote.py
import subprocess
import time
from dbterm import termremo
def openremote(hostaddress, serverport, myname, conntype):
try:
server_address = hostaddress
server_port = serverport
# MSTSC 실행 명령어
mstsc_command = f"mstsc /admin /v:{server_address}:{server_port}"
# MSTSC 실행
mstsc_process = subprocess.Popen(mstsc_command, shell=True)
# 원격 데스크톱 연결의 PID 찾기
while True:
try:
# MSTSC 프로세스의 상태 확인
if mstsc_process.poll() is not None:
break
# MSTSC 실행 중인 프로세스의 PID 찾기
result = subprocess.run(
'wmic process where "CommandLine like \'%mstsc%\' and not CommandLine like \'%wmic%\'" get ProcessId /format:value',
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True
)
output = result.stdout.strip()
rdp_pid = int(output.split('=')[1])
except ValueError:
# 정수로 변환할 수 없는 경우 계속 시도
pass
except IndexError:
pass
finally:
print(f"MSTSC 프로세스가 종료되었습니다")
termremo(myname, conntype)
if __name__ == "__main__":
openremote()
setup.py
from cx_Freeze import setup, Executable
import sys
buildOptions = {
"packages":[
'subprocess','pymysql','datetime','time','os','win32gui', 'win32con',"tkinter"
],
"excludes":[
]
}
exe = [Executable('main.py', base='Win32GUI',icon="icon.ico")]
setup(
name='main',
version='1.0',
author='me',
options = dict(build_exe = buildOptions),
executables = exe
)
userinfo.txt
username : 이재원
'WORK > ✍🏻 기록' 카테고리의 다른 글
식권관리 키오스크 만들기(1) - 웹페이지 제작 (0) | 2024.12.31 |
---|---|
Google Sheets 활용기 (0) | 2024.07.03 |
VSCode 스니펫, 자동완성 키워드 생성 (0) | 2024.04.16 |
리액트 컴포넌트화 (0) | 2024.04.11 |
Redmine 활용기 (0) | 2024.03.25 |