들어가기 앞서
Hyper-V를 자동화 하는것이 프로젝트의 가장 핵심적인 과제였고, 앞서나간 이들이 없었기 때문에 사실상 맨땅에 헤딩이었다.
사실상 이 기능을 구현하지 못하면 앞서 만든 기능들이 쓸모 없어지기 때문에 심리적으로 많이 고생한것 같았다.
결론을 먼저 말하자면 밤새 여러 시도끝에 성공하고 치킨을 시켰으나 아침해가 떠서 열어있는 치킨집이 없었다고 한다. ㅎㅎ
Hyper-V Cmdlet이 되는지 확인좀 해봅시다
Hyper-V를 컨트롤 할 수 있는 powershell Cmdlet의 document의
링크를
동봉해
드리겠
습니다
Hyper-V Module
Use this topic to help manage Windows and Windows Server technologies with Windows PowerShell.
docs.microsoft.com
그래서 이게 되냐 안되냐에 대한 불안감은 뒤로 한 채, 일단 cmdlet을 이용하여 VM을 생성할 수 있는지 확인이 필요했다.
요구사항은 다음과 같았다.
- 유저가 원하는 VM이름을 지정할것
- 유저가 선택한 옵션에 맞는 메모리, CPU코어, 대역폭을 지정하여 생성할 것
- vlanId를 2로 지정할것
- 2세대로 생성할 것
-기타 default 설정을 만족할 것
create_Vm.ps1:
$image = "C:\Users\Public\Documents\Hyper-V\iso\Windows_Server_2016_Datacenter_EVAL_en-us_14393_refresh.iso"
$VM = @{
Name = $VMName
MemoryStartupBytes = $MemoryStartupBytes
Generation = 2
NewVHDPath = "D:\$VMName.vhdx"
NewVHDSizeBytes = $NewVHDSizeBytes #storage
BootDevice = "VHD"
Path = "C:\ProgramData\Microsoft\Windows\Hyper-V\"
SwitchName = "Default Switch"
}
New-VM @VM
Set-VMNetworkAdapterVlan -VMName $VMName -Access -VlanId 2
Set-VM -VMName $VMName -ProcessorCount $ProcessCore
Set-VMNetworkAdapter -Vmname $VMName -MaximumBandwidth $traffic
Set-VMProcessor $VMName -Count $ProcessCore
Set-VMDvdDrive -Vmname $VMName -path $image
(실제로는 값들이 들어가야 할 위치에 보는 여러분들로 하여금 이해하기 편하도록 $변수 로 대체하였읍니다.)
이 스크립트가 실행되었느냐고 물으면 반은 맞고 반은 아니라고 할수 있었다.
관리자 권한이 있는 powershell에 한줄한줄 직접 입력하면 작동하긴 했다.
하지만 단순히 스크립트를 실행시키면 관리자 권한으로 실행되지 않았다.
Hyper-V를 powershell로 제어하기 위해서는 관리자권한으로 powershell을 실행해야했기 때문이다.
방법을 찾아내는데 꽤나 오랜 시간이 걸렸기 때문에, 여러분도 함께 고민해 보셨으면 해서(나만 힘들순 없으니까요)
시행착오를 적어보려 한다.
원래 김칠복의 의도는 이러했다
app.py:
@app.route('/create', methods=['GET','POST'])
def create():
user_id = session.get('login',None)
if user_id == None:
return redirect(url_for("login"))
if request.method == 'GET':
return render_template('create.html',user_info = user.find_one({"user_id":user_id}), price = admin.find_one({"lable":"price"}))
else:
result = user.find_one( {'user_id' : user_id})
service_num = request.form['service_num']
nums = number_changer(service_num)
price = admin.find_one({"lable":"price"})
auto = request.form['auto']
autos = boolean_changer(auto)
host_id = request.form['host_id']
host_pw = request.form['host_pw']
host_pw2 = request.form['host_pw2']
end_time = datetime.today() + relativedelta(months= +1)
start_time = datetime.today()
pw_hash = hashlib.sha256(host_pw.encode('utf-8')).hexdigest()
if host_pw != host_pw2:
flash("호스트 비밀번호가 다릅니다.")
return render_template('create.html',user_info = user.find_one({"user_id":user_id}), price = admin.find_one({"lable":"price"}))
if result["point"] >= price[nums] :
Vm.insert_one({
'user_id':user_id,
'host_pw': pw_hash,
'host_id': host_id,
'auto' : autos,
'service_num' : nums,
'start_time' : start_time,
'end_time': end_time,
'trans':True,
'running':True})
#
#여기쯤??
#
sleep(3)
after_point = result["point"] - price[nums]
user.update_one({"user_id":user_id},{"$set":{"point":after_point}})
Vm.update_one({"trans":True},{"$set":{"trans":False}})
flash("서버가 성공적으로 생성되었습니다.")
return redirect(url_for("main"))
else:
flash("포인트가 부족합니다.")
return redirect(url_for('create'))
주석으로 여기쯤?이라 적어둔 곳에 미리 만들어둔 ps1파일을 실행시키고, 짠! 하고 만들어진다는 아이디어이다.
아주 희망찬 계획이었으나 먹구름이 드리우고 있었다.
Try 1: powershell 명령어중 관리자 권한을 얻는 명령어를 찾아서 ps1파일에 추가시키자!
물론 그런건 없었다. 정말 열심히 찾아봤지만 그런 코드같은건 존재하지 않았다. 생각해보면 그런게 있으면 윈도우는 보안에 매우매우 취약해질게 당연했다. 이쯤에서 3시간을 썼지만 나는 포기하지 않았다.
Try 2: python에서 powershell 스크립트를 실행시킬때 관리자 권한으로 실행해보자!
검색했더니 비슷한 글들이 stackoverflow에 보여서 나는 일말의 가능성을 보기 시작했다. 신은 나를 버리지 않은것이 분명했다.

오오...뭐야... 저걸로 하면 되어버리는거 아니야?
내용물:

우선 cmd 명령중 powershell 스크립트를 관리자 권한으로 실행하는 명령을 os.system으로 넘긴다는 발상인거같았다.
응용해봤지만 윈도우 보안에 막혀버린다. 이 외에도 파이썬 코드내에서 subprocess에서 runAs로 권한을 얻고 시작하는 모든 시도들도 전부 무의미하게 짓밟혔다. 이쯤 추가로 5시간을 썼지만 나는 포기하지 않았다.
다른 방법을 강구해야했다.
Try 3: subprocess.Popen()을 이용해보자!

이 답변이 해결책이 될 줄 알았으나, 이번에도 막혀버렸다. administrator 계정으로 접속하는 아이디어였으나, 나중에 서버가 가동 될 로컬 컴퓨터의 계정을 변경하는데 제약이 있을예정이라 이 방법은 안될것이다. 이쯤에서 한시간을 더 사용했으나 포기하지 않았다.
Try 4: CMD 스크립트 (BAT) 파일을 이용해보자! (드디어 성공)
이 아이디어는 파워쉘 커맨드를 run As로 실행하는 두번째 시도에서 발상을 얻었다.
파이썬 내에서 runAs가 막힌다면, 파이썬 밖에서 하면 되지 않을까 했던 것이다.
ps1 파일을 관리자로 실행하는 CMD 명령어는 한줄이면 되지만, 생각보다 복잡한 작성이 필요했다.
지금부터 조금 헷갈릴 수 있다.
cd "C:파일경로"; & ".\스크립트.ps1"
이 코드를 A라고 하자.
A코드는 관리자 권한 파워쉘에서 실행되어야 하기 때문에, 다음의 절차를 밟아야 한다.
1) cmd 에서 powershell 실행
2) powershell 에서 관리자권한 powershell 실행
3) 관리자권한 powershell에서 관리자권한 powershell에 A코드를 관리자권한으로 실행하도록 넘김
4) 관리자권한 powershell에서 A코드 실행
위 절차중 하나만 생략해도 코드는 작동하지 않는다. 섬세한 구현이 필요했다.
create.bat:
powershell -Command "Start-Process powershell \"-ExecutionPolicy Bypass -NoProfile -Command `\"cd \`\"C:파일경로\`\"; & \`\".\스크립트.ps1\`\"`\"\" -Verb RunAs"
위와같은 create.bat을 실행하면 다음과 같은 일이 벌어진다.
코드 | 실행주체 | 동작 | ||||
powershell -Command "Start-Process powershell \"-ExecutionPolicy Bypass -NoProfile -Command `\"cd \`\"C:파일경로\`\"; & \`\".\스크립트.ps1\`\"`\"\" -Verb RunAs" | cmd | powershell 창을 열어 다음 코드 실행 |
||||
Start-Process powershell "-ExecutionPolicy Bypass -NoProfile -Command `"cd \`"C:파일경로\`"; & \`".\스크립트.ps1\`"`"" -Verb RunAs | powershell | 관리자권한이 있는 powershell을 실행하여 다음 코드 실행 |
||||
-ExecutionPolicy Bypass -NoProfile -Command "cd \"C:파일경로\"; & \".\스크립트.ps1\"" | powershell (관리자) |
관리자 권한있는 새 powershell 세션 에 다음 코드 전달 | ||||
cd "C:파일경로"; & ".\스크립트.ps1" | powershell (관리자) |
관리자 권한으로 ps1파일 실행 |
껍데기가 한겹씩 벗겨지듯이 의도한 코드가 결국 관리자 권한의 powershell에서 실행된다.
위에서 언급한 #여기쯤? 에는 간단하게 bat파일을 실행하는 코드 한줄만 집어넣으면 끝이다.
subprocess.call([".\create.bat"])
다음 글에서는 powershell 스크립트 내에서 MongoDB를 다루도록 하겠다. 아직 자동화로 가기에는 한 고비만 넘겼을 뿐이다.
'프로젝트 > hyper-V 웹서비스' 카테고리의 다른 글
[hyper-V 웹 서비스]6.결제 API 이해하기 (0) | 2022.03.17 |
---|---|
[hyper-V 웹 서비스]5.Hyper-v 자동생성 기능 구현 알고리즘 -2 (0) | 2022.03.17 |
[hyper-V 웹서비스]3. flask로 MongoDB에서 가져온 정보 html에 넘겨주기 (0) | 2022.03.11 |
[hyper-V 웹서비스]2. flask로 로그인 기능 구현 (0) | 2022.03.11 |
[hyper-V 웹서비스]1. flask로 회원가입 기능 구현 (0) | 2022.03.11 |