forked from uncloud/uncloud
meow
cc0ca68498
* Fix issue that causes a new image store to be created at every start of ucloud-api. * VM Migration API call now takes hostname instead of host key. * StorageHandler Classes are introduced. They transparently handles things related to importing of image, make vm out of image, resize vm image, delete vm image etc. * Loggers added to __init__.py of every ucloud component's subpackage. * Non-Trivial Timeout Events are no longer logged. * Fix issue that prevents removal of stopped VMs (i.e VMs that are successfully migrated). * Improved unit handling added. e.g MB, Mb, mB, mb are all Mega Bytes. * VM migration is now possible on IPv6 host. * Destination VM (receiving side of migration of a vm) now correctly expects incoming data on free ephemeral port. * Traceback is no longer output to screen, instead it goes to log file. * All sanity checks are put into a single file. These checks are run by ucloud.py before running any of ucloud component.
113 lines
3.6 KiB
Python
Executable file
113 lines
3.6 KiB
Python
Executable file
from collections import Counter
|
|
from functools import reduce
|
|
|
|
import bitmath
|
|
|
|
from common.host import HostStatus
|
|
from common.request import RequestEntry, RequestType
|
|
from common.vm import VMStatus
|
|
from config import vm_pool, host_pool, request_pool, env_vars
|
|
|
|
|
|
def accumulated_specs(vms_specs):
|
|
if not vms_specs:
|
|
return {}
|
|
return reduce((lambda x, y: Counter(x) + Counter(y)), vms_specs)
|
|
|
|
|
|
def remaining_resources(host_specs, vms_specs):
|
|
# Return remaining resources host_specs - vms
|
|
|
|
_vms_specs = Counter(vms_specs)
|
|
_remaining = Counter(host_specs)
|
|
|
|
for component in _vms_specs:
|
|
if isinstance(_vms_specs[component], str):
|
|
_vms_specs[component] = int(bitmath.parse_string_unsafe(_vms_specs[component]).to_MB())
|
|
elif isinstance(_vms_specs[component], list):
|
|
_vms_specs[component] = map(lambda x: int(bitmath.parse_string_unsafe(x).to_MB()), _vms_specs[component])
|
|
_vms_specs[component] = reduce(lambda x, y: x + y, _vms_specs[component], 0)
|
|
|
|
for component in _remaining:
|
|
if isinstance(_remaining[component], str):
|
|
_remaining[component] = int(bitmath.parse_string_unsafe(_remaining[component]).to_MB())
|
|
elif isinstance(_remaining[component], list):
|
|
_remaining[component] = map(lambda x: int(bitmath.parse_string_unsafe(x).to_MB()), _remaining[component])
|
|
_remaining[component] = reduce(lambda x, y: x + y, _remaining[component], 0)
|
|
|
|
_remaining.subtract(_vms_specs)
|
|
|
|
return _remaining
|
|
|
|
|
|
class NoSuitableHostFound(Exception):
|
|
"""Exception when no host found that can host a VM."""
|
|
|
|
|
|
def get_suitable_host(vm_specs, hosts=None):
|
|
if hosts is None:
|
|
hosts = host_pool.by_status(HostStatus.alive)
|
|
|
|
for host in hosts:
|
|
# Filter them by host_name
|
|
vms = vm_pool.by_host(host.key)
|
|
|
|
# Filter them by status
|
|
vms = vm_pool.by_status(VMStatus.running, vms)
|
|
|
|
running_vms_specs = [vm.specs for vm in vms]
|
|
|
|
# Accumulate all of their combined specs
|
|
running_vms_accumulated_specs = accumulated_specs(running_vms_specs)
|
|
|
|
# Find out remaining resources after
|
|
# host_specs - already running vm_specs
|
|
remaining = remaining_resources(host.specs, running_vms_accumulated_specs)
|
|
|
|
# Find out remaining - new_vm_specs
|
|
remaining = remaining_resources(remaining, vm_specs)
|
|
|
|
if all(map(lambda x: x >= 0, remaining.values())):
|
|
return host.key
|
|
|
|
raise NoSuitableHostFound
|
|
|
|
|
|
def dead_host_detection():
|
|
# Bring out your dead! - Monty Python and the Holy Grail
|
|
hosts = host_pool.by_status(HostStatus.alive)
|
|
dead_hosts_keys = []
|
|
|
|
for host in hosts:
|
|
# Only check those who claims to be alive
|
|
if host.status == HostStatus.alive:
|
|
if not host.is_alive():
|
|
dead_hosts_keys.append(host.key)
|
|
|
|
return dead_hosts_keys
|
|
|
|
|
|
def dead_host_mitigation(dead_hosts_keys):
|
|
for host_key in dead_hosts_keys:
|
|
host = host_pool.get(host_key)
|
|
host.declare_dead()
|
|
|
|
vms_hosted_on_dead_host = vm_pool.by_host(host_key)
|
|
for vm in vms_hosted_on_dead_host:
|
|
vm.declare_killed()
|
|
vm_pool.put(vm)
|
|
host_pool.put(host)
|
|
|
|
|
|
def assign_host(vm):
|
|
vm.hostname = get_suitable_host(vm.specs)
|
|
vm_pool.put(vm)
|
|
|
|
r = RequestEntry.from_scratch(type=RequestType.StartVM,
|
|
uuid=vm.uuid,
|
|
hostname=vm.hostname,
|
|
request_prefix=env_vars.get("REQUEST_PREFIX"))
|
|
request_pool.put(r)
|
|
|
|
vm.log.append("VM scheduled for starting")
|
|
return vm.hostname
|