2019-08-12 13:35:35 +00:00
|
|
|
from contextlib import contextmanager
|
|
|
|
from datetime import datetime
|
2019-07-30 13:13:05 +00:00
|
|
|
from etcd3_wrapper import EtcdEntry
|
|
|
|
from .helpers import SpecificEtcdEntryBase
|
2019-08-12 13:35:35 +00:00
|
|
|
from os.path import join
|
2019-07-30 13:13:05 +00:00
|
|
|
|
|
|
|
|
|
|
|
class VMStatus(object):
|
|
|
|
# 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_migrate = "REQUESTED_MIGRATE"
|
2019-08-12 13:35:35 +00:00
|
|
|
requested_delete = "REQUESTED_DELETE"
|
2019-07-30 13:13:05 +00:00
|
|
|
# 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"
|
|
|
|
|
|
|
|
|
|
|
|
class VMEntry(SpecificEtcdEntryBase):
|
|
|
|
def __init__(self, e: EtcdEntry):
|
|
|
|
self.owner = ""
|
|
|
|
self.specs = dict()
|
|
|
|
self.hostname = ""
|
|
|
|
self.status = ""
|
|
|
|
self.image_uuid = ""
|
2019-08-12 13:35:35 +00:00
|
|
|
self.log = []
|
|
|
|
self.in_migration = False
|
2019-07-30 13:13:05 +00:00
|
|
|
super().__init__(e)
|
2019-08-12 13:35:35 +00:00
|
|
|
|
2019-07-30 13:13:05 +00:00
|
|
|
@property
|
|
|
|
def uuid(self):
|
|
|
|
return self.key.split("/")[-1]
|
|
|
|
|
|
|
|
def declare_killed(self):
|
|
|
|
self.hostname = ""
|
2019-08-12 13:35:35 +00:00
|
|
|
self.in_migration = False
|
|
|
|
if self.status == VMStatus.running:
|
2019-07-30 13:13:05 +00:00
|
|
|
self.status = VMStatus.killed
|
|
|
|
|
2019-08-12 13:35:35 +00:00
|
|
|
def declare_stopped(self):
|
|
|
|
self.hostname = ""
|
|
|
|
self.in_migration = False
|
|
|
|
self.status = VMStatus.stopped
|
|
|
|
|
|
|
|
def add_log(self, msg):
|
|
|
|
self.log = self.log[:5]
|
|
|
|
self.log.append(f"{datetime.now().isoformat()} - {msg}")
|
|
|
|
|
|
|
|
@property
|
|
|
|
def path(self):
|
|
|
|
return f"rbd:uservms/{self.uuid}"
|
|
|
|
|
2019-07-30 13:13:05 +00:00
|
|
|
|
|
|
|
class VmPool(object):
|
|
|
|
def __init__(self, etcd_client, vm_prefix):
|
|
|
|
self.client = etcd_client
|
|
|
|
self.prefix = vm_prefix
|
|
|
|
|
|
|
|
@property
|
|
|
|
def vms(self):
|
|
|
|
_vms = self.client.get_prefix(self.prefix, value_in_json=True)
|
|
|
|
return [VMEntry(vm) for vm in _vms]
|
|
|
|
|
|
|
|
def by_host(self, host, _vms=None):
|
|
|
|
if _vms is None:
|
|
|
|
_vms = self.vms
|
|
|
|
return list(filter(lambda x: x.hostname == host, _vms))
|
|
|
|
|
|
|
|
def by_status(self, status, _vms=None):
|
|
|
|
if _vms is None:
|
|
|
|
_vms = self.vms
|
|
|
|
return list(filter(lambda x: x.status == status, _vms))
|
|
|
|
|
|
|
|
def except_status(self, status, _vms=None):
|
|
|
|
if _vms is None:
|
|
|
|
_vms = self.vms
|
|
|
|
return list(filter(lambda x: x.status != status, _vms))
|
|
|
|
|
|
|
|
def get(self, key):
|
2019-08-12 13:35:35 +00:00
|
|
|
if not key.startswith(self.prefix):
|
|
|
|
key = join(self.prefix, key)
|
2019-07-30 13:13:05 +00:00
|
|
|
v = self.client.get(key, value_in_json=True)
|
2019-08-12 13:35:35 +00:00
|
|
|
if v:
|
|
|
|
return VMEntry(v)
|
|
|
|
|
2019-07-30 13:13:05 +00:00
|
|
|
def put(self, obj: VMEntry):
|
|
|
|
self.client.put(obj.key, obj.value, value_in_json=True)
|
2019-08-12 13:35:35 +00:00
|
|
|
|
|
|
|
@contextmanager
|
|
|
|
def get_put(self, key) -> VMEntry:
|
|
|
|
# Updates object at key on exit
|
|
|
|
obj = self.get(key)
|
|
|
|
yield obj
|
|
|
|
if obj:
|
|
|
|
self.put(obj)
|