Shutdown Source VM (PAUSED) on successfull migration + blackened all .py files
This commit is contained in:
parent
29e938dc74
commit
9bdf4d2180
31 changed files with 1307 additions and 638 deletions
|
|
@ -15,49 +15,79 @@ from . import virtualmachine, logger
|
|||
def update_heartbeat(hostname):
|
||||
"""Update Last HeartBeat Time for :param hostname: in etcd"""
|
||||
host_pool = shared.host_pool
|
||||
this_host = next(filter(lambda h: h.hostname == hostname, host_pool.hosts), None)
|
||||
this_host = next(
|
||||
filter(lambda h: h.hostname == hostname, host_pool.hosts), None
|
||||
)
|
||||
while True:
|
||||
this_host.update_heartbeat()
|
||||
host_pool.put(this_host)
|
||||
time.sleep(10)
|
||||
|
||||
|
||||
def maintenance():
|
||||
def maintenance(host):
|
||||
vmm = VMM()
|
||||
running_vms = vmm.discover()
|
||||
for vm_uuid in running_vms:
|
||||
if vmm.is_running(vm_uuid) and vmm.get_status(vm_uuid) == 'running':
|
||||
vm = shared.vm_pool.get(join_path(settings['etcd']['vm_prefix'], vm_uuid))
|
||||
if (
|
||||
vmm.is_running(vm_uuid)
|
||||
and vmm.get_status(vm_uuid) == "running"
|
||||
):
|
||||
vm = shared.vm_pool.get(
|
||||
join_path(settings["etcd"]["vm_prefix"], vm_uuid)
|
||||
)
|
||||
vm.status = VMStatus.running
|
||||
vm.vnc_socket = vmm.get_vnc(vm_uuid)
|
||||
vm.hostname = host
|
||||
shared.vm_pool.put(vm)
|
||||
|
||||
|
||||
def main(hostname):
|
||||
host_pool = shared.host_pool
|
||||
host = next(filter(lambda h: h.hostname == hostname, host_pool.hosts), None)
|
||||
assert host is not None, "No such host with name = {}".format(hostname)
|
||||
host = next(
|
||||
filter(lambda h: h.hostname == hostname, host_pool.hosts), None
|
||||
)
|
||||
assert host is not None, "No such host with name = {}".format(
|
||||
hostname
|
||||
)
|
||||
|
||||
try:
|
||||
heartbeat_updating_process = mp.Process(target=update_heartbeat, args=(hostname,))
|
||||
heartbeat_updating_process = mp.Process(
|
||||
target=update_heartbeat, args=(hostname,)
|
||||
)
|
||||
heartbeat_updating_process.start()
|
||||
except Exception as e:
|
||||
raise Exception('ucloud-host heartbeat updating mechanism is not working') from e
|
||||
raise Exception(
|
||||
"ucloud-host heartbeat updating mechanism is not working"
|
||||
) from e
|
||||
|
||||
for events_iterator in [
|
||||
shared.etcd_client.get_prefix(settings['etcd']['request_prefix'], value_in_json=True),
|
||||
shared.etcd_client.watch_prefix(settings['etcd']['request_prefix'], timeout=10, value_in_json=True),
|
||||
shared.etcd_client.get_prefix(
|
||||
settings["etcd"]["request_prefix"], value_in_json=True
|
||||
),
|
||||
shared.etcd_client.watch_prefix(
|
||||
settings["etcd"]["request_prefix"],
|
||||
timeout=10,
|
||||
value_in_json=True,
|
||||
),
|
||||
]:
|
||||
for request_event in events_iterator:
|
||||
request_event = RequestEntry(request_event)
|
||||
|
||||
if request_event.type == "TIMEOUT":
|
||||
maintenance()
|
||||
maintenance(host.key)
|
||||
|
||||
if request_event.hostname == host.key:
|
||||
logger.debug("VM Request: %s", request_event)
|
||||
|
||||
shared.request_pool.client.client.delete(request_event.key)
|
||||
vm_entry = shared.etcd_client.get(join_path(settings['etcd']['vm_prefix'], request_event.uuid))
|
||||
shared.request_pool.client.client.delete(
|
||||
request_event.key
|
||||
)
|
||||
vm_entry = shared.etcd_client.get(
|
||||
join_path(
|
||||
settings["etcd"]["vm_prefix"],
|
||||
request_event.uuid,
|
||||
)
|
||||
)
|
||||
|
||||
if vm_entry:
|
||||
vm = virtualmachine.VM(vm_entry)
|
||||
|
|
@ -70,23 +100,35 @@ def main(hostname):
|
|||
elif request_event.type == RequestType.DeleteVM:
|
||||
vm.delete()
|
||||
|
||||
elif request_event.type == RequestType.InitVMMigration:
|
||||
elif (
|
||||
request_event.type
|
||||
== RequestType.InitVMMigration
|
||||
):
|
||||
vm.start(destination_host_key=host.key)
|
||||
|
||||
elif request_event.type == RequestType.TransferVM:
|
||||
host = host_pool.get(request_event.destination_host_key)
|
||||
host = host_pool.get(
|
||||
request_event.destination_host_key
|
||||
)
|
||||
if host:
|
||||
vm.migrate(destination_host=host.hostname,
|
||||
destination_sock_path=request_event.destination_sock_path)
|
||||
vm.migrate(
|
||||
destination_host=host.hostname,
|
||||
destination_sock_path=request_event.destination_sock_path,
|
||||
)
|
||||
else:
|
||||
logger.error('Host %s not found!', request_event.destination_host_key)
|
||||
logger.error(
|
||||
"Host %s not found!",
|
||||
request_event.destination_host_key,
|
||||
)
|
||||
else:
|
||||
logger.info("VM Entry missing")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
argparser = argparse.ArgumentParser()
|
||||
argparser.add_argument("hostname", help="Name of this host. e.g uncloud1.ungleich.ch")
|
||||
argparser.add_argument(
|
||||
"hostname", help="Name of this host. e.g uncloud1.ungleich.ch"
|
||||
)
|
||||
args = argparser.parse_args()
|
||||
mp.set_start_method('spawn')
|
||||
mp.set_start_method("spawn")
|
||||
main(args.hostname)
|
||||
|
|
|
|||
|
|
@ -26,10 +26,7 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
# Mapping host architecture to any additional architectures it can
|
||||
# support which often includes its 32 bit cousin.
|
||||
ADDITIONAL_ARCHES = {
|
||||
"x86_64": "i386",
|
||||
"aarch64": "armhf"
|
||||
}
|
||||
ADDITIONAL_ARCHES = {"x86_64": "i386", "aarch64": "armhf"}
|
||||
|
||||
|
||||
def kvm_available(target_arch=None):
|
||||
|
|
@ -81,10 +78,17 @@ class QEMUMachine(object):
|
|||
# vm is guaranteed to be shut down here
|
||||
"""
|
||||
|
||||
def __init__(self, binary, args=None, wrapper=None, name=None,
|
||||
test_dir="/var/tmp", monitor_address=None,
|
||||
socket_scm_helper=None):
|
||||
'''
|
||||
def __init__(
|
||||
self,
|
||||
binary,
|
||||
args=None,
|
||||
wrapper=None,
|
||||
name=None,
|
||||
test_dir="/var/tmp",
|
||||
monitor_address=None,
|
||||
socket_scm_helper=None,
|
||||
):
|
||||
"""
|
||||
Initialize a QEMUMachine
|
||||
|
||||
@param binary: path to the qemu binary
|
||||
|
|
@ -95,7 +99,7 @@ class QEMUMachine(object):
|
|||
@param monitor_address: address for QMP monitor
|
||||
@param socket_scm_helper: helper program, required for send_fd_scm()
|
||||
@note: Qemu process is not started until launch() is used.
|
||||
'''
|
||||
"""
|
||||
if args is None:
|
||||
args = []
|
||||
if wrapper is None:
|
||||
|
|
@ -109,7 +113,9 @@ class QEMUMachine(object):
|
|||
self._qemu_log_file = None
|
||||
self._popen = None
|
||||
self._binary = binary
|
||||
self._args = list(args) # Force copy args in case we modify them
|
||||
self._args = list(
|
||||
args
|
||||
) # Force copy args in case we modify them
|
||||
self._wrapper = wrapper
|
||||
self._events = []
|
||||
self._iolog = None
|
||||
|
|
@ -137,26 +143,24 @@ class QEMUMachine(object):
|
|||
|
||||
# This can be used to add an unused monitor instance.
|
||||
def add_monitor_null(self):
|
||||
self._args.append('-monitor')
|
||||
self._args.append('null')
|
||||
self._args.append("-monitor")
|
||||
self._args.append("null")
|
||||
|
||||
def add_fd(self, fd, fdset, opaque, opts=''):
|
||||
def add_fd(self, fd, fdset, opaque, opts=""):
|
||||
"""
|
||||
Pass a file descriptor to the VM
|
||||
"""
|
||||
options = ['fd=%d' % fd,
|
||||
'set=%d' % fdset,
|
||||
'opaque=%s' % opaque]
|
||||
options = ["fd=%d" % fd, "set=%d" % fdset, "opaque=%s" % opaque]
|
||||
if opts:
|
||||
options.append(opts)
|
||||
|
||||
# This did not exist before 3.4, but since then it is
|
||||
# mandatory for our purpose
|
||||
if hasattr(os, 'set_inheritable'):
|
||||
if hasattr(os, "set_inheritable"):
|
||||
os.set_inheritable(fd, True)
|
||||
|
||||
self._args.append('-add-fd')
|
||||
self._args.append(','.join(options))
|
||||
self._args.append("-add-fd")
|
||||
self._args.append(",".join(options))
|
||||
return self
|
||||
|
||||
# Exactly one of fd and file_path must be given.
|
||||
|
|
@ -168,18 +172,21 @@ class QEMUMachine(object):
|
|||
if self._socket_scm_helper is None:
|
||||
raise QEMUMachineError("No path to socket_scm_helper set")
|
||||
if not os.path.exists(self._socket_scm_helper):
|
||||
raise QEMUMachineError("%s does not exist" %
|
||||
self._socket_scm_helper)
|
||||
raise QEMUMachineError(
|
||||
"%s does not exist" % self._socket_scm_helper
|
||||
)
|
||||
|
||||
# This did not exist before 3.4, but since then it is
|
||||
# mandatory for our purpose
|
||||
if hasattr(os, 'set_inheritable'):
|
||||
if hasattr(os, "set_inheritable"):
|
||||
os.set_inheritable(self._qmp.get_sock_fd(), True)
|
||||
if fd is not None:
|
||||
os.set_inheritable(fd, True)
|
||||
|
||||
fd_param = ["%s" % self._socket_scm_helper,
|
||||
"%d" % self._qmp.get_sock_fd()]
|
||||
fd_param = [
|
||||
"%s" % self._socket_scm_helper,
|
||||
"%d" % self._qmp.get_sock_fd(),
|
||||
]
|
||||
|
||||
if file_path is not None:
|
||||
assert fd is None
|
||||
|
|
@ -188,9 +195,14 @@ class QEMUMachine(object):
|
|||
assert fd is not None
|
||||
fd_param.append(str(fd))
|
||||
|
||||
devnull = open(os.path.devnull, 'rb')
|
||||
proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, close_fds=False)
|
||||
devnull = open(os.path.devnull, "rb")
|
||||
proc = subprocess.Popen(
|
||||
fd_param,
|
||||
stdin=devnull,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
close_fds=False,
|
||||
)
|
||||
output = proc.communicate()[0]
|
||||
if output:
|
||||
LOG.debug(output)
|
||||
|
|
@ -231,24 +243,29 @@ class QEMUMachine(object):
|
|||
if isinstance(self._monitor_address, tuple):
|
||||
moncdev = "socket,id=mon,host=%s,port=%s" % (
|
||||
self._monitor_address[0],
|
||||
self._monitor_address[1])
|
||||
self._monitor_address[1],
|
||||
)
|
||||
else:
|
||||
moncdev = 'socket,id=mon,path=%s' % self._vm_monitor
|
||||
args = ['-chardev', moncdev,
|
||||
'-mon', 'chardev=mon,mode=control']
|
||||
moncdev = "socket,id=mon,path=%s" % self._vm_monitor
|
||||
args = ["-chardev", moncdev, "-mon", "chardev=mon,mode=control"]
|
||||
if self._machine is not None:
|
||||
args.extend(['-machine', self._machine])
|
||||
args.extend(["-machine", self._machine])
|
||||
if self._console_set:
|
||||
self._console_address = os.path.join(self._temp_dir,
|
||||
self._name + "-console.sock")
|
||||
chardev = ('socket,id=console,path=%s,server,nowait' %
|
||||
self._console_address)
|
||||
args.extend(['-chardev', chardev])
|
||||
self._console_address = os.path.join(
|
||||
self._temp_dir, self._name + "-console.sock"
|
||||
)
|
||||
chardev = (
|
||||
"socket,id=console,path=%s,server,nowait"
|
||||
% self._console_address
|
||||
)
|
||||
args.extend(["-chardev", chardev])
|
||||
if self._console_device_type is None:
|
||||
args.extend(['-serial', 'chardev:console'])
|
||||
args.extend(["-serial", "chardev:console"])
|
||||
else:
|
||||
device = '%s,chardev=console' % self._console_device_type
|
||||
args.extend(['-device', device])
|
||||
device = (
|
||||
"%s,chardev=console" % self._console_device_type
|
||||
)
|
||||
args.extend(["-device", device])
|
||||
return args
|
||||
|
||||
def _pre_launch(self):
|
||||
|
|
@ -256,13 +273,17 @@ class QEMUMachine(object):
|
|||
if self._monitor_address is not None:
|
||||
self._vm_monitor = self._monitor_address
|
||||
else:
|
||||
self._vm_monitor = os.path.join(self._temp_dir,
|
||||
self._name + "-monitor.sock")
|
||||
self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log")
|
||||
self._qemu_log_file = open(self._qemu_log_path, 'wb')
|
||||
self._vm_monitor = os.path.join(
|
||||
self._temp_dir, self._name + "-monitor.sock"
|
||||
)
|
||||
self._qemu_log_path = os.path.join(
|
||||
self._temp_dir, self._name + ".log"
|
||||
)
|
||||
self._qemu_log_file = open(self._qemu_log_path, "wb")
|
||||
|
||||
self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor,
|
||||
server=True)
|
||||
self._qmp = qmp.QEMUMonitorProtocol(
|
||||
self._vm_monitor, server=True
|
||||
)
|
||||
|
||||
def _post_launch(self):
|
||||
self._qmp.accept()
|
||||
|
|
@ -289,7 +310,7 @@ class QEMUMachine(object):
|
|||
"""
|
||||
|
||||
if self._launched:
|
||||
raise QEMUMachineError('VM already launched')
|
||||
raise QEMUMachineError("VM already launched")
|
||||
|
||||
self._iolog = None
|
||||
self._qemu_full_args = None
|
||||
|
|
@ -299,11 +320,11 @@ class QEMUMachine(object):
|
|||
except:
|
||||
self.shutdown()
|
||||
|
||||
LOG.debug('Error launching VM')
|
||||
LOG.debug("Error launching VM")
|
||||
if self._qemu_full_args:
|
||||
LOG.debug('Command: %r', ' '.join(self._qemu_full_args))
|
||||
LOG.debug("Command: %r", " ".join(self._qemu_full_args))
|
||||
if self._iolog:
|
||||
LOG.debug('Output: %r', self._iolog)
|
||||
LOG.debug("Output: %r", self._iolog)
|
||||
raise Exception(self._iolog)
|
||||
raise
|
||||
|
||||
|
|
@ -311,17 +332,25 @@ class QEMUMachine(object):
|
|||
"""
|
||||
Launch the VM and establish a QMP connection
|
||||
"""
|
||||
devnull = open(os.path.devnull, 'rb')
|
||||
devnull = open(os.path.devnull, "rb")
|
||||
self._pre_launch()
|
||||
self._qemu_full_args = (self._wrapper + [self._binary] +
|
||||
self._base_args() + self._args)
|
||||
LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args))
|
||||
self._popen = subprocess.Popen(self._qemu_full_args,
|
||||
stdin=devnull,
|
||||
stdout=self._qemu_log_file,
|
||||
stderr=subprocess.STDOUT,
|
||||
shell=False,
|
||||
close_fds=False)
|
||||
self._qemu_full_args = (
|
||||
self._wrapper
|
||||
+ [self._binary]
|
||||
+ self._base_args()
|
||||
+ self._args
|
||||
)
|
||||
LOG.debug(
|
||||
"VM launch command: %r", " ".join(self._qemu_full_args)
|
||||
)
|
||||
self._popen = subprocess.Popen(
|
||||
self._qemu_full_args,
|
||||
stdin=devnull,
|
||||
stdout=self._qemu_log_file,
|
||||
stderr=subprocess.STDOUT,
|
||||
shell=False,
|
||||
close_fds=False,
|
||||
)
|
||||
self._post_launch()
|
||||
|
||||
def wait(self):
|
||||
|
|
@ -339,7 +368,7 @@ class QEMUMachine(object):
|
|||
"""
|
||||
if self.is_running():
|
||||
try:
|
||||
self._qmp.cmd('quit')
|
||||
self._qmp.cmd("quit")
|
||||
self._qmp.close()
|
||||
except:
|
||||
self._popen.kill()
|
||||
|
|
@ -350,11 +379,11 @@ class QEMUMachine(object):
|
|||
|
||||
exitcode = self.exitcode()
|
||||
if exitcode is not None and exitcode < 0:
|
||||
msg = 'qemu received signal %i: %s'
|
||||
msg = "qemu received signal %i: %s"
|
||||
if self._qemu_full_args:
|
||||
command = ' '.join(self._qemu_full_args)
|
||||
command = " ".join(self._qemu_full_args)
|
||||
else:
|
||||
command = ''
|
||||
command = ""
|
||||
LOG.warn(msg, -exitcode, command)
|
||||
|
||||
self._launched = False
|
||||
|
|
@ -366,7 +395,7 @@ class QEMUMachine(object):
|
|||
qmp_args = dict()
|
||||
for key, value in args.items():
|
||||
if conv_keys:
|
||||
qmp_args[key.replace('_', '-')] = value
|
||||
qmp_args[key.replace("_", "-")] = value
|
||||
else:
|
||||
qmp_args[key] = value
|
||||
|
||||
|
|
@ -427,7 +456,9 @@ class QEMUMachine(object):
|
|||
try:
|
||||
for key in match:
|
||||
if key in event:
|
||||
if not QEMUMachine.event_match(event[key], match[key]):
|
||||
if not QEMUMachine.event_match(
|
||||
event[key], match[key]
|
||||
):
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
|
@ -458,8 +489,9 @@ class QEMUMachine(object):
|
|||
|
||||
def _match(event):
|
||||
for name, match in events:
|
||||
if (event['event'] == name and
|
||||
self.event_match(event, match)):
|
||||
if event["event"] == name and self.event_match(
|
||||
event, match
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
@ -531,7 +563,8 @@ class QEMUMachine(object):
|
|||
Returns a socket connected to the console
|
||||
"""
|
||||
if self._console_socket is None:
|
||||
self._console_socket = socket.socket(socket.AF_UNIX,
|
||||
socket.SOCK_STREAM)
|
||||
self._console_socket = socket.socket(
|
||||
socket.AF_UNIX, socket.SOCK_STREAM
|
||||
)
|
||||
self._console_socket.connect(self._console_address)
|
||||
return self._console_socket
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class QMPTimeoutError(QMPError):
|
|||
|
||||
class QEMUMonitorProtocol(object):
|
||||
#: Logger object for debugging messages
|
||||
logger = logging.getLogger('QMP')
|
||||
logger = logging.getLogger("QMP")
|
||||
#: Socket's error class
|
||||
error = socket.error
|
||||
#: Socket's timeout
|
||||
|
|
@ -55,7 +55,9 @@ class QEMUMonitorProtocol(object):
|
|||
self.__sock = self.__get_sock()
|
||||
self.__sockfile = None
|
||||
if server:
|
||||
self.__sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
self.__sock.setsockopt(
|
||||
socket.SOL_SOCKET, socket.SO_REUSEADDR, 1
|
||||
)
|
||||
self.__sock.bind(self.__address)
|
||||
self.__sock.listen(1)
|
||||
|
||||
|
|
@ -71,7 +73,7 @@ class QEMUMonitorProtocol(object):
|
|||
if greeting is None or "QMP" not in greeting:
|
||||
raise QMPConnectError
|
||||
# Greeting seems ok, negotiate capabilities
|
||||
resp = self.cmd('qmp_capabilities')
|
||||
resp = self.cmd("qmp_capabilities")
|
||||
if "return" in resp:
|
||||
return greeting
|
||||
raise QMPCapabilitiesError
|
||||
|
|
@ -82,7 +84,7 @@ class QEMUMonitorProtocol(object):
|
|||
if not data:
|
||||
return
|
||||
resp = json.loads(data)
|
||||
if 'event' in resp:
|
||||
if "event" in resp:
|
||||
self.logger.debug("<<< %s", resp)
|
||||
self.__events.append(resp)
|
||||
if not only_event:
|
||||
|
|
@ -165,7 +167,7 @@ class QEMUMonitorProtocol(object):
|
|||
"""
|
||||
self.logger.debug(">>> %s", qmp_cmd)
|
||||
try:
|
||||
self.__sock.sendall(json.dumps(qmp_cmd).encode('utf-8'))
|
||||
self.__sock.sendall(json.dumps(qmp_cmd).encode("utf-8"))
|
||||
except socket.error as err:
|
||||
if err[0] == errno.EPIPE:
|
||||
return
|
||||
|
|
@ -182,11 +184,11 @@ class QEMUMonitorProtocol(object):
|
|||
@param args: command arguments (dict)
|
||||
@param cmd_id: command id (dict, list, string or int)
|
||||
"""
|
||||
qmp_cmd = {'execute': name}
|
||||
qmp_cmd = {"execute": name}
|
||||
if args:
|
||||
qmp_cmd['arguments'] = args
|
||||
qmp_cmd["arguments"] = args
|
||||
if cmd_id:
|
||||
qmp_cmd['id'] = cmd_id
|
||||
qmp_cmd["id"] = cmd_id
|
||||
return self.cmd_obj(qmp_cmd)
|
||||
|
||||
def command(self, cmd, **kwds):
|
||||
|
|
@ -195,8 +197,8 @@ class QEMUMonitorProtocol(object):
|
|||
"""
|
||||
ret = self.cmd(cmd, kwds)
|
||||
if "error" in ret:
|
||||
raise Exception(ret['error']['desc'])
|
||||
return ret['return']
|
||||
raise Exception(ret["error"]["desc"])
|
||||
return ret["return"]
|
||||
|
||||
def pull_event(self, wait=False):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -23,10 +23,6 @@ from ucloud.vmm import VMM
|
|||
from marshmallow import ValidationError
|
||||
|
||||
|
||||
def maintenance():
|
||||
pass
|
||||
|
||||
|
||||
class VM:
|
||||
def __init__(self, vm_entry):
|
||||
self.schema = VMSchema()
|
||||
|
|
@ -35,23 +31,30 @@ class VM:
|
|||
try:
|
||||
self.vm = self.schema.loads(vm_entry.value)
|
||||
except ValidationError:
|
||||
logger.exception('Couldn\'t validate VM Entry', vm_entry.value)
|
||||
logger.exception(
|
||||
"Couldn't validate VM Entry", vm_entry.value
|
||||
)
|
||||
self.vm = None
|
||||
else:
|
||||
self.uuid = vm_entry.key.split('/')[-1]
|
||||
self.host_key = self.vm['hostname']
|
||||
self.uuid = vm_entry.key.split("/")[-1]
|
||||
self.host_key = self.vm["hostname"]
|
||||
|
||||
def get_qemu_args(self):
|
||||
command = (
|
||||
'-name {owner}_{name}'
|
||||
' -drive file={file},format=raw,if=virtio,cache=none'
|
||||
' -device virtio-rng-pci'
|
||||
' -m {memory} -smp cores={cores},threads={threads}'
|
||||
).format(owner=self.vm['owner'], name=self.vm['name'],
|
||||
memory=int(self.vm['specs']['ram'].to_MB()), cores=self.vm['specs']['cpu'],
|
||||
threads=1, file=shared.storage_handler.qemu_path_string(self.uuid))
|
||||
"-name {owner}_{name}"
|
||||
" -drive file={file},format=raw,if=virtio,cache=none"
|
||||
" -device virtio-rng-pci"
|
||||
" -m {memory} -smp cores={cores},threads={threads}"
|
||||
).format(
|
||||
owner=self.vm["owner"],
|
||||
name=self.vm["name"],
|
||||
memory=int(self.vm["specs"]["ram"].to_MB()),
|
||||
cores=self.vm["specs"]["cpu"],
|
||||
threads=1,
|
||||
file=shared.storage_handler.qemu_path_string(self.uuid),
|
||||
)
|
||||
|
||||
return command.split(' ')
|
||||
return command.split(" ")
|
||||
|
||||
def start(self, destination_host_key=None):
|
||||
migration = False
|
||||
|
|
@ -63,24 +66,34 @@ class VM:
|
|||
network_args = self.create_network_dev()
|
||||
except Exception as err:
|
||||
declare_stopped(self.vm)
|
||||
self.vm['log'].append('Cannot Setup Network Properly')
|
||||
logger.error('Cannot Setup Network Properly for vm %s', self.uuid, exc_info=err)
|
||||
self.vm["log"].append("Cannot Setup Network Properly")
|
||||
logger.error(
|
||||
"Cannot Setup Network Properly for vm %s",
|
||||
self.uuid,
|
||||
exc_info=err,
|
||||
)
|
||||
else:
|
||||
self.vmm.start(uuid=self.uuid, migration=migration,
|
||||
*self.get_qemu_args(), *network_args)
|
||||
self.vmm.start(
|
||||
uuid=self.uuid,
|
||||
migration=migration,
|
||||
*self.get_qemu_args(),
|
||||
*network_args
|
||||
)
|
||||
|
||||
status = self.vmm.get_status(self.uuid)
|
||||
if status == 'running':
|
||||
self.vm['status'] = VMStatus.running
|
||||
self.vm['vnc_socket'] = self.vmm.get_vnc(self.uuid)
|
||||
elif status == 'inmigrate':
|
||||
if status == "running":
|
||||
self.vm["status"] = VMStatus.running
|
||||
self.vm["vnc_socket"] = self.vmm.get_vnc(self.uuid)
|
||||
elif status == "inmigrate":
|
||||
r = RequestEntry.from_scratch(
|
||||
type=RequestType.TransferVM, # Transfer VM
|
||||
hostname=self.host_key, # Which VM should get this request. It is source host
|
||||
uuid=self.uuid, # uuid of VM
|
||||
destination_sock_path=join_path(self.vmm.socket_dir, self.uuid),
|
||||
destination_sock_path=join_path(
|
||||
self.vmm.socket_dir, self.uuid
|
||||
),
|
||||
destination_host_key=destination_host_key, # Where source host transfer VM
|
||||
request_prefix=settings['etcd']['request_prefix']
|
||||
request_prefix=settings["etcd"]["request_prefix"],
|
||||
)
|
||||
shared.request_pool.put(r)
|
||||
else:
|
||||
|
|
@ -96,15 +109,22 @@ class VM:
|
|||
self.sync()
|
||||
|
||||
def migrate(self, destination_host, destination_sock_path):
|
||||
self.vmm.transfer(src_uuid=self.uuid, destination_sock_path=destination_sock_path,
|
||||
host=destination_host)
|
||||
self.vmm.transfer(
|
||||
src_uuid=self.uuid,
|
||||
destination_sock_path=destination_sock_path,
|
||||
host=destination_host,
|
||||
)
|
||||
|
||||
def create_network_dev(self):
|
||||
command = ''
|
||||
for network_mac_and_tap in self.vm['network']:
|
||||
command = ""
|
||||
for network_mac_and_tap in self.vm["network"]:
|
||||
network_name, mac, tap = network_mac_and_tap
|
||||
|
||||
_key = os.path.join(settings['etcd']['network_prefix'], self.vm['owner'], network_name)
|
||||
_key = os.path.join(
|
||||
settings["etcd"]["network_prefix"],
|
||||
self.vm["owner"],
|
||||
network_name,
|
||||
)
|
||||
network = shared.etcd_client.get(_key, value_in_json=True)
|
||||
network_schema = NetworkSchema()
|
||||
try:
|
||||
|
|
@ -112,49 +132,64 @@ class VM:
|
|||
except ValidationError:
|
||||
continue
|
||||
|
||||
if network['type'] == "vxlan":
|
||||
tap = create_vxlan_br_tap(_id=network['id'],
|
||||
_dev=settings['network']['vxlan_phy_dev'],
|
||||
tap_id=tap,
|
||||
ip=network['ipv6'])
|
||||
if network["type"] == "vxlan":
|
||||
tap = create_vxlan_br_tap(
|
||||
_id=network["id"],
|
||||
_dev=settings["network"]["vxlan_phy_dev"],
|
||||
tap_id=tap,
|
||||
ip=network["ipv6"],
|
||||
)
|
||||
|
||||
all_networks = shared.etcd_client.get_prefix(settings['etcd']['network_prefix'],
|
||||
value_in_json=True)
|
||||
all_networks = shared.etcd_client.get_prefix(
|
||||
settings["etcd"]["network_prefix"],
|
||||
value_in_json=True,
|
||||
)
|
||||
|
||||
if ipaddress.ip_network(network['ipv6']).is_global:
|
||||
if ipaddress.ip_network(network["ipv6"]).is_global:
|
||||
update_radvd_conf(all_networks)
|
||||
|
||||
command += '-netdev tap,id=vmnet{net_id},ifname={tap},script=no,downscript=no' \
|
||||
' -device virtio-net-pci,netdev=vmnet{net_id},mac={mac}' \
|
||||
.format(tap=tap, net_id=network['id'], mac=mac)
|
||||
command += (
|
||||
"-netdev tap,id=vmnet{net_id},ifname={tap},script=no,downscript=no"
|
||||
" -device virtio-net-pci,netdev=vmnet{net_id},mac={mac}".format(
|
||||
tap=tap, net_id=network["id"], mac=mac
|
||||
)
|
||||
)
|
||||
|
||||
return command.split(' ')
|
||||
return command.split(" ")
|
||||
|
||||
def delete_network_dev(self):
|
||||
try:
|
||||
for network in self.vm['network']:
|
||||
for network in self.vm["network"]:
|
||||
network_name = network[0]
|
||||
_ = network[1] # tap_mac
|
||||
tap_id = network[2]
|
||||
|
||||
delete_network_interface('tap{}'.format(tap_id))
|
||||
delete_network_interface("tap{}".format(tap_id))
|
||||
|
||||
owners_vms = shared.vm_pool.by_owner(self.vm['owner'])
|
||||
owners_running_vms = shared.vm_pool.by_status(VMStatus.running,
|
||||
_vms=owners_vms)
|
||||
owners_vms = shared.vm_pool.by_owner(self.vm["owner"])
|
||||
owners_running_vms = shared.vm_pool.by_status(
|
||||
VMStatus.running, _vms=owners_vms
|
||||
)
|
||||
|
||||
networks = map(
|
||||
lambda n: n[0], map(lambda vm: vm.network, owners_running_vms)
|
||||
lambda n: n[0],
|
||||
map(lambda vm: vm.network, owners_running_vms),
|
||||
)
|
||||
networks_in_use_by_user_vms = [vm[0] for vm in networks]
|
||||
if network_name not in networks_in_use_by_user_vms:
|
||||
network_entry = resolve_network(network[0], self.vm['owner'])
|
||||
network_entry = resolve_network(
|
||||
network[0], self.vm["owner"]
|
||||
)
|
||||
if network_entry:
|
||||
network_type = network_entry.value["type"]
|
||||
network_id = network_entry.value["id"]
|
||||
if network_type == "vxlan":
|
||||
delete_network_interface('br{}'.format(network_id))
|
||||
delete_network_interface('vxlan{}'.format(network_id))
|
||||
delete_network_interface(
|
||||
"br{}".format(network_id)
|
||||
)
|
||||
delete_network_interface(
|
||||
"vxlan{}".format(network_id)
|
||||
)
|
||||
except Exception:
|
||||
logger.exception("Exception in network interface deletion")
|
||||
|
||||
|
|
@ -163,15 +198,21 @@ class VM:
|
|||
# File Already exists. No Problem Continue
|
||||
logger.debug("Image for vm %s exists", self.uuid)
|
||||
else:
|
||||
if shared.storage_handler.make_vm_image(src=self.vm['image_uuid'], dest=self.uuid):
|
||||
if not shared.storage_handler.resize_vm_image(path=self.uuid,
|
||||
size=int(self.vm['specs']['os-ssd'].to_MB())):
|
||||
self.vm['status'] = VMStatus.error
|
||||
if shared.storage_handler.make_vm_image(
|
||||
src=self.vm["image_uuid"], dest=self.uuid
|
||||
):
|
||||
if not shared.storage_handler.resize_vm_image(
|
||||
path=self.uuid,
|
||||
size=int(self.vm["specs"]["os-ssd"].to_MB()),
|
||||
):
|
||||
self.vm["status"] = VMStatus.error
|
||||
else:
|
||||
logger.info("New VM Created")
|
||||
|
||||
def sync(self):
|
||||
shared.etcd_client.put(self.key, self.schema.dump(self.vm), value_in_json=True)
|
||||
shared.etcd_client.put(
|
||||
self.key, self.schema.dump(self.vm), value_in_json=True
|
||||
)
|
||||
|
||||
def delete(self):
|
||||
self.stop()
|
||||
|
|
@ -186,50 +227,77 @@ class VM:
|
|||
|
||||
def resolve_network(network_name, network_owner):
|
||||
network = shared.etcd_client.get(
|
||||
join_path(settings['etcd']['network_prefix'], network_owner, network_name), value_in_json=True
|
||||
join_path(
|
||||
settings["etcd"]["network_prefix"],
|
||||
network_owner,
|
||||
network_name,
|
||||
),
|
||||
value_in_json=True,
|
||||
)
|
||||
return network
|
||||
|
||||
|
||||
def create_vxlan_br_tap(_id, _dev, tap_id, ip=None):
|
||||
network_script_base = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'network')
|
||||
vxlan = create_dev(script=os.path.join(network_script_base, 'create-vxlan.sh'),
|
||||
_id=_id, dev=_dev)
|
||||
network_script_base = os.path.join(
|
||||
os.path.dirname(os.path.dirname(__file__)), "network"
|
||||
)
|
||||
vxlan = create_dev(
|
||||
script=os.path.join(network_script_base, "create-vxlan.sh"),
|
||||
_id=_id,
|
||||
dev=_dev,
|
||||
)
|
||||
if vxlan:
|
||||
bridge = create_dev(script=os.path.join(network_script_base, 'create-bridge.sh'),
|
||||
_id=_id, dev=vxlan, ip=ip)
|
||||
bridge = create_dev(
|
||||
script=os.path.join(
|
||||
network_script_base, "create-bridge.sh"
|
||||
),
|
||||
_id=_id,
|
||||
dev=vxlan,
|
||||
ip=ip,
|
||||
)
|
||||
if bridge:
|
||||
tap = create_dev(script=os.path.join(network_script_base, 'create-tap.sh'),
|
||||
_id=str(tap_id), dev=bridge)
|
||||
tap = create_dev(
|
||||
script=os.path.join(
|
||||
network_script_base, "create-tap.sh"
|
||||
),
|
||||
_id=str(tap_id),
|
||||
dev=bridge,
|
||||
)
|
||||
if tap:
|
||||
return tap
|
||||
|
||||
|
||||
def update_radvd_conf(all_networks):
|
||||
network_script_base = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'network')
|
||||
network_script_base = os.path.join(
|
||||
os.path.dirname(os.path.dirname(__file__)), "network"
|
||||
)
|
||||
|
||||
networks = {
|
||||
net.value['ipv6']: net.value['id']
|
||||
net.value["ipv6"]: net.value["id"]
|
||||
for net in all_networks
|
||||
if net.value.get('ipv6') and ipaddress.ip_network(net.value.get('ipv6')).is_global
|
||||
if net.value.get("ipv6")
|
||||
and ipaddress.ip_network(net.value.get("ipv6")).is_global
|
||||
}
|
||||
radvd_template = open(os.path.join(network_script_base,
|
||||
'radvd-template.conf'), 'r').read()
|
||||
radvd_template = open(
|
||||
os.path.join(network_script_base, "radvd-template.conf"), "r"
|
||||
).read()
|
||||
radvd_template = Template(radvd_template)
|
||||
|
||||
content = [
|
||||
radvd_template.safe_substitute(
|
||||
bridge='br{}'.format(networks[net]),
|
||||
prefix=net
|
||||
bridge="br{}".format(networks[net]), prefix=net
|
||||
)
|
||||
for net in networks if networks.get(net)
|
||||
for net in networks
|
||||
if networks.get(net)
|
||||
]
|
||||
with open('/etc/radvd.conf', 'w') as radvd_conf:
|
||||
with open("/etc/radvd.conf", "w") as radvd_conf:
|
||||
radvd_conf.writelines(content)
|
||||
try:
|
||||
sp.check_output(['systemctl', 'restart', 'radvd'])
|
||||
sp.check_output(["systemctl", "restart", "radvd"])
|
||||
except sp.CalledProcessError:
|
||||
try:
|
||||
sp.check_output(['service', 'radvd', 'restart'])
|
||||
sp.check_output(["service", "radvd", "restart"])
|
||||
except sp.CalledProcessError as err:
|
||||
raise err.__class__('Cannot start/restart radvd service', err.cmd) from err
|
||||
raise err.__class__(
|
||||
"Cannot start/restart radvd service", err.cmd
|
||||
) from err
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue