working till migration
This commit is contained in:
		
					parent
					
						
							
								b8a44eca69
							
						
					
				
			
			
				commit
				
					
						1dddc2197b
					
				
			
		
					 10 changed files with 10857 additions and 3407 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -6,3 +6,5 @@ venv/
 | 
			
		|||
 | 
			
		||||
log.txt
 | 
			
		||||
vm_socklog/
 | 
			
		||||
etcd3_wrapper/
 | 
			
		||||
ucloud_common/
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -1,3 +0,0 @@
 | 
			
		|||
[submodule "etcd3_wrapper"]
 | 
			
		||||
	path = etcd3_wrapper
 | 
			
		||||
	url = https://code.ungleich.ch/ahmedbilal/etcd3_wrapper
 | 
			
		||||
							
								
								
									
										3
									
								
								Pipfile
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								Pipfile
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -5,12 +5,13 @@ verify_ssl = true
 | 
			
		|||
 | 
			
		||||
[dev-packages]
 | 
			
		||||
bandit = "*"
 | 
			
		||||
pylama = "*"
 | 
			
		||||
 | 
			
		||||
[packages]
 | 
			
		||||
python-decouple = "*"
 | 
			
		||||
etcd3 = "*"
 | 
			
		||||
cython = "*"
 | 
			
		||||
pylint = "*"
 | 
			
		||||
python-etcd3 = {editable = true,git = "https://github.com/kragniz/python-etcd3"}
 | 
			
		||||
 | 
			
		||||
[requires]
 | 
			
		||||
python_version = "3.7"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										138
									
								
								Pipfile.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										138
									
								
								Pipfile.lock
									
										
									
										generated
									
									
									
								
							| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
    "_meta": {
 | 
			
		||||
        "hash": {
 | 
			
		||||
            "sha256": "1473a03ca16049e1c2200b57b1403db764cd05a8c3a94d1d008b109127f72f87"
 | 
			
		||||
            "sha256": "6f0726e9d014ad330ab36d11b44a15c59316234e343697e46ea2a10ab0997a90"
 | 
			
		||||
        },
 | 
			
		||||
        "pipfile-spec": 6,
 | 
			
		||||
        "requires": {
 | 
			
		||||
| 
						 | 
				
			
			@ -25,44 +25,37 @@
 | 
			
		|||
        },
 | 
			
		||||
        "cython": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:04ebf16df9406d3279a2489c3327803c782d9e17637d525bfb44ecf5ec65850f",
 | 
			
		||||
                "sha256:1486ec88d1c73dea3846a5640054018b002608e04a791ccbd2082a47bce4440a",
 | 
			
		||||
                "sha256:20da832a5e9a8e93d1e1eb64650258956723940968eb585506531719b55b804f",
 | 
			
		||||
                "sha256:2464688b523d7a133b52cf1343c1c595b92fc6554af1015f74b9e49951e992d4",
 | 
			
		||||
                "sha256:27827b68a8359e9ab6bf683c68d8ee79863a0c94a577acf56aa02cc302e16f51",
 | 
			
		||||
                "sha256:27deeeeca0fd8933af07923e809c8fed0763d150a4fdd4082932a33b8c874ed6",
 | 
			
		||||
                "sha256:31f4da785d5e09deb852ea59795a629c5befb6040929e7880c6f63e6668246ce",
 | 
			
		||||
                "sha256:4828cf8fa638c35139e643f30201b240c0d156b1b9967a7321ae42d721d7224c",
 | 
			
		||||
                "sha256:48b365e32cc5639ae2c239d7bd4f8a1d920a13a7ae92113c4c938903c9400147",
 | 
			
		||||
                "sha256:4eb71856c1d1b33083df9318fd30143470ad6f0d1b9ad2ee61a120710842d28b",
 | 
			
		||||
                "sha256:5b06ef8422d27d8128f8f80bdefa111eadcab246fba1d668720af4f0b97b7a0e",
 | 
			
		||||
                "sha256:71c553640e1ddaaf143e38dbc6cd1863fa3c0738fb1830a9aaffba9a51838f30",
 | 
			
		||||
                "sha256:73e2742ee1f923c5f213183bf493901f9630e395634fce5b739a53b7dc5d64be",
 | 
			
		||||
                "sha256:82a632bc02063eff0b8e7ff3089aa3d912d1c7499709f51c8f04f57c8832cfe6",
 | 
			
		||||
                "sha256:977ca1ac059e4d4a4bf5fe2224986baf42b69290453eda44822606f4deae6515",
 | 
			
		||||
                "sha256:a7e6217d0dd864a7cc4f457172766864496efd64d24d4980df1521f75f992761",
 | 
			
		||||
                "sha256:ad0ed7dd5dff76eb3aae8c18d95b1c9f885a91a92132728051a704fb8060d08c",
 | 
			
		||||
                "sha256:b1b8eda9e931f0ca1aadb95a890811bdf530407e48c962643b85675329d99abf",
 | 
			
		||||
                "sha256:cec99c79205131da3ee75becea1f3f55c57bf6a1c500431de9ae7a32ac8a5cc4",
 | 
			
		||||
                "sha256:d4bbdaa6f61ce2ef26535a7d473d6ffa6e413013c5c580af999546bf1627ae11",
 | 
			
		||||
                "sha256:d8bdb4208975b12048bdace46e9dd8e3dda3872432f95b53789700a1330e6060",
 | 
			
		||||
                "sha256:dce0362ff9b61f8411d1efc9e16fc528dadbd3707a557561992457f5cb446297",
 | 
			
		||||
                "sha256:defbbbf5653629ce5cc54091ce49c6830da8d3104de53ed2169c9efcb0720f27",
 | 
			
		||||
                "sha256:e0c53a7e2b6d82ec3c26c009c937fc88eb8c7edf000c54334261beaf56bb08f2",
 | 
			
		||||
                "sha256:e1065bacfe5303f107896e63263537dee90920d26050f2e23c4af12c37da2db6",
 | 
			
		||||
                "sha256:e142837c4212c0b2c71e6773cb6740828922806b4c00ee4215be3ceb558671e6",
 | 
			
		||||
                "sha256:f4cbbab28c93ffee6ec929cf0826f0b11d2488e53a708d51142a5e62f8cd9806",
 | 
			
		||||
                "sha256:fa8f63b6551621eea9efea4db37ae401104352f0ebaee32f7d20be88cbe589c3"
 | 
			
		||||
                "sha256:07efba7b32c082c519b75e3b03821c2f32848e2b3e9986c784bbd8ffaf0666d7",
 | 
			
		||||
                "sha256:08db41daf18fabf7b7a85e39aa26954f6246994540043194af026c0df65a4942",
 | 
			
		||||
                "sha256:19bbe3caf885a1d2e2c30eacc10d1e45dbbefb156493fe1d5d1adc1668cc1269",
 | 
			
		||||
                "sha256:1c574f2f2ba760b82b2bcf6262e77e75589247dc5ef796a3ff1b2213e50ee452",
 | 
			
		||||
                "sha256:1dfe672c686e34598bdbaa93c3b30acb3720ae9258232a4f68ba04ee9969063d",
 | 
			
		||||
                "sha256:283faea84e6c4e54c3f5c8ff89aa2b6c1c3a813aad4f6d48ed3b9cc9043ef9f9",
 | 
			
		||||
                "sha256:2a145888d0942e7c36e86a7b7c7e2923cb9f7055805a3b72dcb137e3efdb0979",
 | 
			
		||||
                "sha256:3f75065936e16569d6e13dfd76de988f5eabeae460aa54770c9b961ab6f747fc",
 | 
			
		||||
                "sha256:4d78124f5f281f1d5d5b7919cbbc65a7073ff93562def81ee78a8307e6e72494",
 | 
			
		||||
                "sha256:5ba4d088b8e5d59b8a5911ca9c72952acf3c83296b57daf75af92fb2af1e8423",
 | 
			
		||||
                "sha256:6b19daeda1d5d1dfc973b291246f6a63a663b20c33980724d6d073c562719536",
 | 
			
		||||
                "sha256:790c7dc80fd1c3e38acefe06027e2f5a8466c128c7e47c6e140fd5316132574d",
 | 
			
		||||
                "sha256:7f8c4e648881454ba3ba0bcf3b21a9e1878a67d20ea2b8d9ec1c4c628592ab6b",
 | 
			
		||||
                "sha256:8bcd3f597290f9902548d6355898d7e376e7f3762f89db9cd50b2b58429df9e8",
 | 
			
		||||
                "sha256:8ffb18f71972a5c718a8600d9f52e3507f0d6fb72a978e03270d34a7035c98fb",
 | 
			
		||||
                "sha256:92f025df1cb391e09f65775598c7dfb7efad72d74713775db54e267f62ca94a1",
 | 
			
		||||
                "sha256:93cf1c72472a2fd0ef4c52f6074dab08fc28d475b9c824ba73a52701f7a48ae1",
 | 
			
		||||
                "sha256:9a7fa692cdc967fdbf6a053c1975137d01f6935dede2ef222c71840b290caf79",
 | 
			
		||||
                "sha256:a68eb0c1375f2401de881692b30370a51e550052b8e346b2f71bbdbdc74a214f",
 | 
			
		||||
                "sha256:ac3b7a12ddd52ea910ee3a041e6bc65df7a52f0ba7bd10fb7123502af482c152",
 | 
			
		||||
                "sha256:b402b700edaf571a0bae18ec35d5b71c266873a6616412b672435c10b6d8f041",
 | 
			
		||||
                "sha256:c29d069a4a30f472482343c866f7486731ad638ef9af92bfe5fca9c7323d638e",
 | 
			
		||||
                "sha256:d822311498f185db449b687336b4e5db7638c8d8b03bdf10ae91d74e23c7cc0c",
 | 
			
		||||
                "sha256:dccc8df9e1ac158b06777bbaaeb4516f245f9b147701ae25e6023960e4a0c2a3",
 | 
			
		||||
                "sha256:e31f4b946c2765b2f35440fdb4b00c496dfc5babc53c7ae61966b41171d1d59f",
 | 
			
		||||
                "sha256:eb43f9e582cc221ee2832e25ea6fe5c06f2acc9da6353c562e922f107db12af8",
 | 
			
		||||
                "sha256:f07822248110fd6213db8bc2745fdbbccef6f2b3d18ac91a7fba29c6bc575da5",
 | 
			
		||||
                "sha256:ff69854f123b959d4ae14bd5330714bb9ee4360052992dc0fbd0a3dee4261f95"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.29.12"
 | 
			
		||||
        },
 | 
			
		||||
        "etcd3": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:25a524b9f032c6631ff0097532907dea81243eaa63c3744510fd1598cc4e0e87"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==0.10.0"
 | 
			
		||||
            "version": "==0.29.13"
 | 
			
		||||
        },
 | 
			
		||||
        "grpcio": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
| 
						 | 
				
			
			@ -176,6 +169,11 @@
 | 
			
		|||
            "index": "pypi",
 | 
			
		||||
            "version": "==3.1"
 | 
			
		||||
        },
 | 
			
		||||
        "python-etcd3": {
 | 
			
		||||
            "editable": true,
 | 
			
		||||
            "git": "https://github.com/kragniz/python-etcd3",
 | 
			
		||||
            "ref": "cdc4c48bde88a795230a02aa574df84ed9ccfa52"
 | 
			
		||||
        },
 | 
			
		||||
        "six": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c",
 | 
			
		||||
| 
						 | 
				
			
			@ -227,6 +225,19 @@
 | 
			
		|||
            "index": "pypi",
 | 
			
		||||
            "version": "==1.6.2"
 | 
			
		||||
        },
 | 
			
		||||
        "ddt": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:474546b4020ce8a2f9550ba8899c28aa2c284c7bbf175bddede98be949d1ca7c",
 | 
			
		||||
                "sha256:d13e6af8f36238e89d00f4ebccf2bda4f6d1878be560a6600689e42077e164e3"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==1.2.1"
 | 
			
		||||
        },
 | 
			
		||||
        "gitdb": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:a3ebbc27be035a2e874ed904df516e35f4a29a778a764385de09de9e0f139658"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.6.4"
 | 
			
		||||
        },
 | 
			
		||||
        "gitdb2": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:83361131a1836661a155172932a13c08bda2db3674e4caa32368aa6eb02f38c2",
 | 
			
		||||
| 
						 | 
				
			
			@ -236,10 +247,17 @@
 | 
			
		|||
        },
 | 
			
		||||
        "gitpython": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:563221e5a44369c6b79172f455584c9ebbb122a13368cc82cb4b5addff788f82",
 | 
			
		||||
                "sha256:8237dc5bfd6f1366abeee5624111b9d6879393d84745a507de0fda86043b65a8"
 | 
			
		||||
                "sha256:7428f1cc5e72d53e65c3259d5cebc22fb2b07f973c49d95b3c3d26c64890a3c3",
 | 
			
		||||
                "sha256:a0f744a4941eac5d99033eb0adcbec83bf443ee173fda4292d92a906aedce952"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2.1.11"
 | 
			
		||||
            "version": "==2.1.12"
 | 
			
		||||
        },
 | 
			
		||||
        "mccabe": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
 | 
			
		||||
                "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.6.1"
 | 
			
		||||
        },
 | 
			
		||||
        "pbr": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
| 
						 | 
				
			
			@ -248,6 +266,34 @@
 | 
			
		|||
            ],
 | 
			
		||||
            "version": "==5.4.1"
 | 
			
		||||
        },
 | 
			
		||||
        "pycodestyle": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
 | 
			
		||||
                "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2.5.0"
 | 
			
		||||
        },
 | 
			
		||||
        "pydocstyle": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:58c421dd605eec0bce65df8b8e5371bb7ae421582cdf0ba8d9435ac5b0ffc36a"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==4.0.0"
 | 
			
		||||
        },
 | 
			
		||||
        "pyflakes": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:17dbeb2e3f4d772725c777fabc446d5634d1038f234e77343108ce445ea69ce0",
 | 
			
		||||
                "sha256:d976835886f8c5b31d47970ed689944a0262b5f3afa00a5a7b4dc81e5449f8a2"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==2.1.1"
 | 
			
		||||
        },
 | 
			
		||||
        "pylama": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:9bae53ef9c1a431371d6a8dca406816a60d547147b60a4934721898f553b7d8f",
 | 
			
		||||
                "sha256:fd61c11872d6256b019ef1235be37b77c922ef37ac9797df6bd489996dddeb15"
 | 
			
		||||
            ],
 | 
			
		||||
            "index": "pypi",
 | 
			
		||||
            "version": "==7.7.1"
 | 
			
		||||
        },
 | 
			
		||||
        "pyyaml": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:57acc1d8533cbe51f6662a55434f0dbecfa2b9eaf115bede8f6fd00115a0c0d3",
 | 
			
		||||
| 
						 | 
				
			
			@ -271,6 +317,12 @@
 | 
			
		|||
            ],
 | 
			
		||||
            "version": "==1.12.0"
 | 
			
		||||
        },
 | 
			
		||||
        "smmap": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:0e2b62b497bd5f0afebc002eda4d90df9d209c30ef257e8673c90a6b5c119d62"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==0.9.0"
 | 
			
		||||
        },
 | 
			
		||||
        "smmap2": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:0555a7bf4df71d1ef4218e4807bbf9b201f910174e6e08af2e138d4e517b4dde",
 | 
			
		||||
| 
						 | 
				
			
			@ -278,6 +330,12 @@
 | 
			
		|||
            ],
 | 
			
		||||
            "version": "==2.0.5"
 | 
			
		||||
        },
 | 
			
		||||
        "snowballstemmer": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:9f3b9ffe0809d174f7047e121431acf99c89a7040f0ca84f94ba53a498e6d0c9"
 | 
			
		||||
            ],
 | 
			
		||||
            "version": "==1.9.0"
 | 
			
		||||
        },
 | 
			
		||||
        "stevedore": {
 | 
			
		||||
            "hashes": [
 | 
			
		||||
                "sha256:7be098ff53d87f23d798a7ce7ae5c31f094f3deb92ba18059b1aeb1ca9fec0a0",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1 +0,0 @@
 | 
			
		|||
Subproject commit d079acadf29e6df55c329574604148631d4ad4bc
 | 
			
		||||
							
								
								
									
										1
									
								
								etcd3_wrapper
									
										
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								etcd3_wrapper
									
										
									
									
									
										Symbolic link
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
../etcd3_wrapper/
 | 
			
		||||
							
								
								
									
										266
									
								
								main.py
									
										
									
									
									
								
							
							
						
						
									
										266
									
								
								main.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -6,47 +6,27 @@
 | 
			
		|||
# For QEMU Monitor Protocol Commands Information, See
 | 
			
		||||
# https://qemu.weilnetz.de/doc/qemu-doc.html#pcsys_005fmonitor
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
import argparse
 | 
			
		||||
import qmp
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import subprocess
 | 
			
		||||
import atexit
 | 
			
		||||
import signal
 | 
			
		||||
import threading
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from etcd3_wrapper import Etcd3Wrapper
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
from typing import Union
 | 
			
		||||
from functools import wraps
 | 
			
		||||
from decouple import config
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from ucloud_common.enums import VMStatus, RUNNING_VM_STATUSES
 | 
			
		||||
 | 
			
		||||
from ucloud_common.rbd import RBD
 | 
			
		||||
from ucloud_common.vm import VmPool, VMEntry, VMStatus
 | 
			
		||||
from ucloud_common.host import HostPool, HostEntry
 | 
			
		||||
 | 
			
		||||
running_vms = []
 | 
			
		||||
vnc_port_pool = list(range(0, 100))
 | 
			
		||||
client = Etcd3Wrapper()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class VM:
 | 
			
		||||
    key: str
 | 
			
		||||
    vm: qmp.QEMUMachine
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RBD(object):
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def ls(pool):
 | 
			
		||||
        output = ""
 | 
			
		||||
        try:
 | 
			
		||||
            output = subprocess.check_output(
 | 
			
		||||
                ["rbd", "ls", pool], stderr=subprocess.PIPE
 | 
			
		||||
            ).decode("utf-8").strip()
 | 
			
		||||
        except subprocess.CalledProcessError as e:
 | 
			
		||||
            raise Exception(e.stderr)
 | 
			
		||||
        return output.split("\n")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
VM_POOL = None
 | 
			
		||||
HOST_POOL = None
 | 
			
		||||
 | 
			
		||||
logging.basicConfig(
 | 
			
		||||
    level=logging.DEBUG,
 | 
			
		||||
| 
						 | 
				
			
			@ -56,25 +36,19 @@ logging.basicConfig(
 | 
			
		|||
    datefmt="%d-%b-%y %H:%M:%S",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@dataclass
 | 
			
		||||
class VM:
 | 
			
		||||
    key: str
 | 
			
		||||
    vm: qmp.QEMUMachine
 | 
			
		||||
 | 
			
		||||
def goodbye(host):
 | 
			
		||||
    vms = client.get_prefix("/v1/vm", value_in_json=True)
 | 
			
		||||
    vms = filter(lambda v: v.value["hostname"] == host.key, vms)
 | 
			
		||||
    for vm in vms:
 | 
			
		||||
        vm.value["hostname"] = ""
 | 
			
		||||
 | 
			
		||||
        if vm.value["status"] in RUNNING_VM_STATUSES:
 | 
			
		||||
            vm.value["status"] = VMStatus.requested_start
 | 
			
		||||
def update_heartbeat(host: HostEntry):
 | 
			
		||||
    while True:
 | 
			
		||||
        host.update_heartbeat()
 | 
			
		||||
        HOST_POOL.put(host)
 | 
			
		||||
        time.sleep(10)
 | 
			
		||||
 | 
			
		||||
        client.put(vm.key, vm.value, value_in_json=True)
 | 
			
		||||
    
 | 
			
		||||
    host.value["status"] = "DEAD"
 | 
			
		||||
    host.value["last_heartbeat"] = datetime.utcnow().isoformat()
 | 
			
		||||
    client.put(host.key, json.dumps(host.value))
 | 
			
		||||
 | 
			
		||||
    logging.info(f"Host {host.key} dead! at {host.value['last_heartbeat']}")
 | 
			
		||||
    print("Goodbye")
 | 
			
		||||
    os.kill(os.getpid(), signal.SIGKILL)
 | 
			
		||||
        logging.info(f"Updated last heartbeat time {host.last_heartbeat}")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def need_running_vm(func):
 | 
			
		||||
| 
						 | 
				
			
			@ -86,48 +60,55 @@ def need_running_vm(func):
 | 
			
		|||
                status = vm.vm.command("query-status")
 | 
			
		||||
                logging.debug(f"VM Status Check - {status}")
 | 
			
		||||
            except OSError:
 | 
			
		||||
                logging.info(f"{func.__name__} failed - VM {e.key} - Unknown Error")
 | 
			
		||||
                logging.info(
 | 
			
		||||
                    f"{func.__name__} failed - VM {e.key} - Unknown Error"
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            return func(e)
 | 
			
		||||
        else:
 | 
			
		||||
            logging.info(f"{func.__name__} failed because VM {e.key} is not running")
 | 
			
		||||
            logging.info(
 | 
			
		||||
                f"{func.__name__} failed because VM {e.key} is not running"
 | 
			
		||||
            )
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
    return wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_vm(vm_uuid, e):
 | 
			
		||||
def create_vm(vm):
 | 
			
		||||
    image = client.get(
 | 
			
		||||
        f"/v1/image/{e.value['image_uuid']}", value_in_json=True
 | 
			
		||||
        f"/v1/image/{vm.value['image_uuid']}", value_in_json=True
 | 
			
		||||
    )
 | 
			
		||||
    if image:
 | 
			
		||||
        logging.debug(image)
 | 
			
		||||
        image_uuid = e.value["image_uuid"]
 | 
			
		||||
 | 
			
		||||
        logging.info("Creating New VM...")
 | 
			
		||||
        _command_to_create = f"rbd clone images/{image_uuid}@protected uservms/{vm_uuid}"
 | 
			
		||||
        _command_to_create = f"rbd clone images/{vm.image_uuid}@protected uservms/{vm.uuid}"
 | 
			
		||||
        try:
 | 
			
		||||
            subprocess.call(_command_to_create.split(" "))
 | 
			
		||||
        # TODO: Make it specific
 | 
			
		||||
            vm.status = VMStatus.requested_start
 | 
			
		||||
            VM_POOL.put(vm)
 | 
			
		||||
        except:
 | 
			
		||||
            pass
 | 
			
		||||
        e.value["status"] = "REQUESTED_START"
 | 
			
		||||
    client.put(e.key, json.dumps(e.value))
 | 
			
		||||
            logging.exception("Can't clone image")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def start_vm(vm_path, e):
 | 
			
		||||
    if not vm_path.split("/")[-1] in RBD.ls("uservms"):
 | 
			
		||||
        logging.info(f"Image file of vm {e.key} does not exists")
 | 
			
		||||
        logging.info(f"Setting vm {e.key} status to DELETED")
 | 
			
		||||
        e.value["status"] = "DELETED"
 | 
			
		||||
        client.put(e.key, json.dumps(e.value))
 | 
			
		||||
    try:
 | 
			
		||||
        user_vms = RBD.ls("uservms")
 | 
			
		||||
    except:
 | 
			
		||||
        logging.info("Can't access uservms pool")
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    if not vm_path.split("/")[-1] in user_vms:
 | 
			
		||||
        logging.info(f"Image file of vm {e.key} does not exists")
 | 
			
		||||
        logging.info(f"Deleting vm {e.key}")
 | 
			
		||||
        client.client.delete(e.key)
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    _vm = get_vm(running_vms, e.key)
 | 
			
		||||
    if _vm:
 | 
			
		||||
        logging.info(f"{e.key} already running")
 | 
			
		||||
        e.value["status"] = "RUNNING"
 | 
			
		||||
        client.put(e.key, json.dumps(e.value))
 | 
			
		||||
        e.status = VMStatus.running
 | 
			
		||||
        VM_POOL.put(e)
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # FIXME: There should be better vnc port allocation scheme
 | 
			
		||||
| 
						 | 
				
			
			@ -136,10 +117,13 @@ def start_vm(vm_path, e):
 | 
			
		|||
        test_dir="vm_socklog",
 | 
			
		||||
        args=[
 | 
			
		||||
            vm_path,
 | 
			
		||||
            "-boot", "c",  # First Boot Hard drive
 | 
			
		||||
            "-m", "1024",  # RAM limit
 | 
			
		||||
            "-boot",
 | 
			
		||||
            "c",  # First Boot Hard drive
 | 
			
		||||
            "-m",
 | 
			
		||||
            "1024",  # RAM limit
 | 
			
		||||
            # Ever growing port number
 | 
			
		||||
            "-vnc", f":{vnc_port_pool.pop(0)}",  # Allow VNC
 | 
			
		||||
            "-vnc",
 | 
			
		||||
            f":{vnc_port_pool.pop(0)}",  # Allow VNC
 | 
			
		||||
        ],
 | 
			
		||||
    )
 | 
			
		||||
    try:
 | 
			
		||||
| 
						 | 
				
			
			@ -147,16 +131,16 @@ def start_vm(vm_path, e):
 | 
			
		|||
        vm.launch()
 | 
			
		||||
        if vm.is_running():
 | 
			
		||||
            running_vms.append(VM(e.key, vm))
 | 
			
		||||
            e.value["status"] = "RUNNING"
 | 
			
		||||
            client.put(e.key, e.value, value_in_json=True)
 | 
			
		||||
            e.status = VMStatus.running
 | 
			
		||||
            VM_POOL.put(e)
 | 
			
		||||
        else:
 | 
			
		||||
            e.value["status"] = "KILLED"
 | 
			
		||||
            client.put(e.key, e.value, value_in_json=True)
 | 
			
		||||
            e.declare_killed()
 | 
			
		||||
            VM_POOL.put(e)
 | 
			
		||||
            return
 | 
			
		||||
    except (qmp.QEMUMachineError, TypeError):
 | 
			
		||||
        logging.exception(f"Machine Error Occurred on {e.key}")
 | 
			
		||||
        e.value["status"] = "KILLED"
 | 
			
		||||
        client.put(e.key, e.value, value_in_json=True)
 | 
			
		||||
        e.declare_killed()
 | 
			
		||||
        VM_POOL.put(e)
 | 
			
		||||
    else:
 | 
			
		||||
        logging.info(f"Started Successfully {e.key}")
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -166,8 +150,8 @@ def suspend_vm(e):
 | 
			
		|||
    vm = get_vm(running_vms, e.key)
 | 
			
		||||
    vm.vm.command("stop")
 | 
			
		||||
    if vm.vm.command("query-status")["status"] == "paused":
 | 
			
		||||
        e.value["status"] = "SUSPENDED"
 | 
			
		||||
        client.put(e.key, json.dumps(e.value))
 | 
			
		||||
        e.status = VMStatus.suspended
 | 
			
		||||
        VM_POOL.put(e)
 | 
			
		||||
        logging.info(f"Successfully suspended VM {e.key}")
 | 
			
		||||
    else:
 | 
			
		||||
        logging.info(f"Suspending VM {e.key} failed")
 | 
			
		||||
| 
						 | 
				
			
			@ -178,8 +162,8 @@ def resume_vm(e):
 | 
			
		|||
    vm = get_vm(running_vms, e.key)
 | 
			
		||||
    vm.vm.command("cont")
 | 
			
		||||
    if vm.vm.command("query-status")["status"] == "running":
 | 
			
		||||
        e.value["status"] = "RUNNING"
 | 
			
		||||
        client.put(e.key, json.dumps(e.value))
 | 
			
		||||
        e.status = VMStatus.running
 | 
			
		||||
        VM_POOL.put(e)
 | 
			
		||||
        logging.info(f"Successfully resumed VM {e.key}")
 | 
			
		||||
    else:
 | 
			
		||||
        logging.info(f"Resuming VM {e.key} failed")
 | 
			
		||||
| 
						 | 
				
			
			@ -191,141 +175,105 @@ def shutdown_vm(e):
 | 
			
		|||
    vm.vm.shutdown()
 | 
			
		||||
    if not vm.vm.is_running():
 | 
			
		||||
        logging.info(f"VM {e.key} shutdown successfully")
 | 
			
		||||
        e.value["status"] = "STOPPED"
 | 
			
		||||
        client.put(e.key, json.dumps(e.value))
 | 
			
		||||
        e.status = VMStatus.stopped
 | 
			
		||||
        VM_POOL.put(e)
 | 
			
		||||
        running_vms.remove(vm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def delete_vm(e):
 | 
			
		||||
    #FIXME: Implementation Obseleted after CEPH Integeration
 | 
			
		||||
    # FIXME: Implementation Obseleted after CEPH Integeration
 | 
			
		||||
    logging.info(f"Deleting VM {e.key}")
 | 
			
		||||
    shutdown_vm(e)
 | 
			
		||||
    vm = client.get(e.key, value_in_json=True)
 | 
			
		||||
    if vm:
 | 
			
		||||
        vm_id = e.key.split('/')[-1]
 | 
			
		||||
        vm_owner = e.value['owner']
 | 
			
		||||
        vm_path = f"{config('BASE_DIR')}/{vm_owner}/.vm/{vm_id}"
 | 
			
		||||
 | 
			
		||||
        if os.path.exists(vm_path):
 | 
			
		||||
            os.remove(vm_path)
 | 
			
		||||
        client.client.delete(e.key)
 | 
			
		||||
        logging.info(f"VM {vm.key} deleted")
 | 
			
		||||
    else:
 | 
			
		||||
        logging.info(f"Cannot delete key {e.key} because it doesn't exists")
 | 
			
		||||
    client.client.delete(e.key)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_vm(vm_list: list, vm_key) -> Union[VM, None]:
 | 
			
		||||
    return next((vm for vm in vm_list if vm.key == vm_key), None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def maintenence(e, host):
 | 
			
		||||
    
 | 
			
		||||
    # VMs on this Host
 | 
			
		||||
    _vms = filter(lambda v: v.value["hostname"] == host.key, client.get_prefix("/v1/vm", value_in_json=True))
 | 
			
		||||
    alleged_running_vms = filter(lambda v: v.value["status"] == "RUNNING", _vms)
 | 
			
		||||
    
 | 
			
		||||
    # TODO: Delete this. This was intended to start VMs that
 | 
			
		||||
    #       requested to be started when ucloud-vm is not running.
 | 
			
		||||
    #       This is no longer needed as we check for pending requests
 | 
			
		||||
    #       at the start and handle them.
 | 
			
		||||
    
 | 
			
		||||
    # should_be_running = filter(lambda v: v.value["status"] == "REQUESTED_START", _vms)
 | 
			
		||||
def maintenence(host):
 | 
			
		||||
    _vms = VM_POOL.by_host(host.key)
 | 
			
		||||
    alleged_running_vms = VM_POOL.by_status("RUNNING", _vms)
 | 
			
		||||
 | 
			
		||||
    for vm in alleged_running_vms:
 | 
			
		||||
        _vm = get_vm(running_vms, vm.key)
 | 
			
		||||
        if (_vm and not _vm.vm.is_running()) or _vm is None:
 | 
			
		||||
            logging.debug(f"{_vm} {vm.key}")
 | 
			
		||||
            logging.info(f"{vm.key} is not running but is said to be running")
 | 
			
		||||
            logging.info(
 | 
			
		||||
                f"{vm.key} is not running but is said to be running"
 | 
			
		||||
            )
 | 
			
		||||
            logging.info(f"Updating {vm.key} status to KILLED")
 | 
			
		||||
            vm.value["status"] = "KILLED"
 | 
			
		||||
            client.put(vm.key, json.dumps(vm.value))
 | 
			
		||||
 | 
			
		||||
    # TODO: Delete this. This was intended to start VMs that
 | 
			
		||||
    #       requested to be started when ucloud-vm is not running.
 | 
			
		||||
    #       This is no longer needed as we check for pending requests
 | 
			
		||||
    #       at the start and handle them.
 | 
			
		||||
    
 | 
			
		||||
    # for vm in should_be_running:
 | 
			
		||||
    #     vm_path = f"rbd:uservms/{vm.key.split('/')[-1]}"
 | 
			
		||||
    #     start_vm(vm_path, e)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # TODO: Check whether a vm is running on this host that
 | 
			
		||||
    # is not supposed to be running on this host
 | 
			
		||||
    
 | 
			
		||||
    host.value["status"] = "ALIVE"
 | 
			
		||||
    host.value["last_heartbeat"] = datetime.utcnow().isoformat()
 | 
			
		||||
    client.put(host.key, json.dumps(host.value))
 | 
			
		||||
    logging.info(f"Updated last heartbeat time {host.value['last_heartbeat']}")
 | 
			
		||||
            vm.declare_killed()
 | 
			
		||||
            VM_POOL.put(vm)
 | 
			
		||||
            running_vms.remove(vm)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    argparser = argparse.ArgumentParser()
 | 
			
		||||
    argparser.add_argument("hostname", help="Name of this host. e.g /v1/host/1")
 | 
			
		||||
    argparser.add_argument(
 | 
			
		||||
        "hostname", help="Name of this host. e.g /v1/host/1"
 | 
			
		||||
    )
 | 
			
		||||
    args = argparser.parse_args()
 | 
			
		||||
 | 
			
		||||
    host = client.get(args.hostname, value_in_json=True)
 | 
			
		||||
    global HOST_POOL, VM_POOL
 | 
			
		||||
    HOST_POOL = HostPool(client, "/v1/host")
 | 
			
		||||
    VM_POOL = VmPool(client, "/v1/vm")
 | 
			
		||||
 | 
			
		||||
    host = HOST_POOL.get(args.hostname)
 | 
			
		||||
    if not host:
 | 
			
		||||
        print("No Such Host")
 | 
			
		||||
        exit(1)
 | 
			
		||||
 | 
			
		||||
    host.value["status"] = "ALIVE"
 | 
			
		||||
    host.value["last_heartbeat"] = datetime.utcnow().isoformat()
 | 
			
		||||
    client.put(host.key, host.value, value_in_json=True)
 | 
			
		||||
    # It is seen that under heavy load, timeout event doesn't come
 | 
			
		||||
    # in a predictive manner which delays heart beat update which
 | 
			
		||||
    # in turn misunderstood by scheduler that the host is dead
 | 
			
		||||
    # when it is actually alive. So, to ensure that we update the
 | 
			
		||||
    # heart beat in a predictive manner we start Heart beat updating
 | 
			
		||||
    # mechanism in separated thread
 | 
			
		||||
    heartbeat_updating_thread = threading.Thread(target=update_heartbeat, args=(host,))
 | 
			
		||||
    heartbeat_updating_thread.start()
 | 
			
		||||
 | 
			
		||||
    atexit.register(goodbye, host=host)
 | 
			
		||||
 | 
			
		||||
    for events_iterator in [client.get_prefix("/v1/vm/"),
 | 
			
		||||
                            client.watch_prefix("/v1/vm/", timeout=10)]:
 | 
			
		||||
    for events_iterator in [
 | 
			
		||||
        client.get_prefix("/v1/vm/", value_in_json=True),
 | 
			
		||||
        client.watch_prefix("/v1/vm/", timeout=10, value_in_json=True),
 | 
			
		||||
    ]:
 | 
			
		||||
        for e in events_iterator:
 | 
			
		||||
            # TODO: Should we disable timeout alarm inside
 | 
			
		||||
            # event handling code and enable it while going outside
 | 
			
		||||
            e = VMEntry(e)
 | 
			
		||||
 | 
			
		||||
            try:
 | 
			
		||||
                e.value = json.loads(e.value)
 | 
			
		||||
            except json.JSONDecodeError:
 | 
			
		||||
                logging.error(f"Invalid JSON {e.value}")
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            e_status = e.value["status"]
 | 
			
		||||
 | 
			
		||||
            if e_status == "TIMEOUT":
 | 
			
		||||
                client.client.delete(e.key)
 | 
			
		||||
            if e.status == "TIMEOUT":
 | 
			
		||||
                logging.info("Timeout")
 | 
			
		||||
                maintenence(e, host)
 | 
			
		||||
                maintenence(host)
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            e_hostname = e.value["hostname"]
 | 
			
		||||
            if hasattr(e.value, "migration_destination"):
 | 
			
		||||
                e_migration_destination = e.value["migration_destination"]
 | 
			
		||||
            if hasattr(e, "migration_destination"):
 | 
			
		||||
                e_migration_destination = e.value[
 | 
			
		||||
                    "migration_destination"
 | 
			
		||||
                ]
 | 
			
		||||
            else:
 | 
			
		||||
                e_migration_destination = ""
 | 
			
		||||
 | 
			
		||||
            vm_uuid = e.key.split("/")[-1]
 | 
			
		||||
 | 
			
		||||
            # If the event is directed toward me or
 | 
			
		||||
            # I am destination of a REQUESTED_MIGRATION
 | 
			
		||||
            if e_hostname == host.key or\
 | 
			
		||||
                e_migration_destination == host.key:
 | 
			
		||||
            if e.hostname == host.key or e_migration_destination == host.key:
 | 
			
		||||
                logging.debug(f"EVENT: {e}")
 | 
			
		||||
 | 
			
		||||
                if e_status == "SCHEDULED_DEPLOY":
 | 
			
		||||
                    create_vm(vm_uuid, e)
 | 
			
		||||
                if e.status == "SCHEDULED_DEPLOY":
 | 
			
		||||
                    create_vm(e)
 | 
			
		||||
 | 
			
		||||
                elif e_status == "REQUESTED_SUSPEND":
 | 
			
		||||
                elif e.status == "REQUESTED_SUSPEND":
 | 
			
		||||
                    suspend_vm(e)
 | 
			
		||||
 | 
			
		||||
                elif e_status == "REQUESTED_RESUME":
 | 
			
		||||
                elif e.status == "REQUESTED_RESUME":
 | 
			
		||||
                    resume_vm(e)
 | 
			
		||||
 | 
			
		||||
                elif e_status == "REQUESTED_START":
 | 
			
		||||
                    vm_path = f"rbd:uservms/{vm_uuid}"
 | 
			
		||||
                elif e.status == "REQUESTED_START":
 | 
			
		||||
                    vm_path = f"rbd:uservms/{e.uuid}"
 | 
			
		||||
                    start_vm(vm_path, e)
 | 
			
		||||
 | 
			
		||||
                elif e_status == "REQUESTED_SHUTDOWN":
 | 
			
		||||
                elif e.status == "REQUESTED_SHUTDOWN":
 | 
			
		||||
                    shutdown_vm(e)
 | 
			
		||||
 | 
			
		||||
                elif e_status == "DELETED":
 | 
			
		||||
                elif e.status == "DELETED":
 | 
			
		||||
                    delete_vm(e)
 | 
			
		||||
 | 
			
		||||
                # elif e_status == "REQUESTED_MIGRATION":
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								ucloud_common
									
										
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								ucloud_common
									
										
									
									
									
										Symbolic link
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
../ucloud_common/
 | 
			
		||||
| 
						 | 
				
			
			@ -1,34 +0,0 @@
 | 
			
		|||
from enum import Enum
 | 
			
		||||
 | 
			
		||||
class VMStatus(Enum):
 | 
			
		||||
    # Must be only assigned to brand new VM
 | 
			
		||||
    requested_new = "REQUESTED_NEW"
 | 
			
		||||
 | 
			
		||||
    # Only Assigned to already created vm
 | 
			
		||||
    requested_start = "REQUESTED_START"
 | 
			
		||||
 | 
			
		||||
    # These all are for running vms
 | 
			
		||||
    requested_shutdown = "REQUESTED_SHUTDOWN"
 | 
			
		||||
    requested_suspend = "REQUESTED_SUSPEND"
 | 
			
		||||
    requested_resume = "REQUESTED_RESUME"
 | 
			
		||||
    requested_migrate = "REQUESTED_MIGRATE"
 | 
			
		||||
 | 
			
		||||
    # either its image is not found or user requested
 | 
			
		||||
    # to delete it
 | 
			
		||||
    deleted = "DELETED"
 | 
			
		||||
    
 | 
			
		||||
    stopped = "STOPPED" # After requested_shutdown
 | 
			
		||||
    killed = "KILLED"  # either host died or vm died itself
 | 
			
		||||
 | 
			
		||||
    running = "RUNNING"
 | 
			
		||||
    suspended = "SUSPENDED"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class HostStatus(Enum):
 | 
			
		||||
    alive = "ALIVE"
 | 
			
		||||
    dead = "DEAD"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
RUNNING_VM_STATUSES = [VMStatus.requested_shutdown, VMStatus.requested_suspend,
 | 
			
		||||
                       VMStatus.requested_resume, VMStatus.requested_migrate,
 | 
			
		||||
                       VMStatus.running, VMStatus.suspended]
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue