Compare commits
10 Commits
05eea7379d
...
a1a503d289
Author | SHA1 | Date |
---|---|---|
|
a1a503d289 | |
|
50e4397145 | |
|
19a25fb269 | |
|
f3a2fdf8d7 | |
|
db160a8b6a | |
|
e04f192194 | |
|
4c1b515007 | |
|
2c2a26b447 | |
|
f02ab9a516 | |
|
3fffc2dc92 |
94
vmmd
94
vmmd
|
@ -332,7 +332,7 @@ def image_info(pathname):
|
|||
pass
|
||||
out = ''
|
||||
try:
|
||||
argv = [find_in_path('qemu-img'), 'info', '-U', pathname]
|
||||
argv = ['qemu-img', 'info', '-U', pathname]
|
||||
(out, err) = cmd_run(argv)
|
||||
except RuntimeError as e:
|
||||
pass
|
||||
|
@ -453,6 +453,8 @@ def pidfile_remove(name, path=None):
|
|||
|
||||
def cmd_run(args, stdin=None):
|
||||
logi("cmd_run: %s\n" % (args))
|
||||
if not args[0].startswith('/'):
|
||||
args[0] = find_in_path(args[0])
|
||||
child = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if not stdin is None:
|
||||
child.stdin.write(stdin)
|
||||
|
@ -464,10 +466,11 @@ def cmd_run(args, stdin=None):
|
|||
|
||||
def fork_child(args):
|
||||
logi("fork_child: %s" % (args))
|
||||
cmd = args[0] if args[0].startswith('/') else find_in_path(args[0])
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
try:
|
||||
os.execv(args[0], args)
|
||||
os.execv(cmd, args)
|
||||
except BaseException as e:
|
||||
sys.stderr.write("os.execv raised %s\n" % (e))
|
||||
sys.stderr.write("os.execv returned unexpectedly\n")
|
||||
|
@ -506,6 +509,13 @@ def sig_term(signum, frame):
|
|||
global running
|
||||
running = False
|
||||
|
||||
def sig_chld(signum, frame):
|
||||
try:
|
||||
while True:
|
||||
(pid, status) = os.waitpid(0, os.WNOHANG)
|
||||
except:
|
||||
pass
|
||||
|
||||
### Config ###
|
||||
|
||||
class Config:
|
||||
|
@ -590,6 +600,9 @@ class DbObject:
|
|||
def values(self):
|
||||
return self._dict.values()
|
||||
|
||||
def __contains__(self, name):
|
||||
return name in self._dict
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self._dict[name]
|
||||
|
||||
|
@ -667,10 +680,8 @@ class DbTable:
|
|||
keystr = ','.join(obj.keys())
|
||||
valstr = ','.join([self._sql_val_str(v) for v in obj.values()])
|
||||
query = "INSERT INTO %s (%s) values (%s)" % (self._name, keystr, valstr)
|
||||
print("query=%s" % (query))
|
||||
cursor = dbconn.execute(query)
|
||||
obj['id'] = cursor.lastrowid
|
||||
print("id=%d" % (cursor.lastrowid))
|
||||
|
||||
def update(self, obj):
|
||||
if not obj._changed:
|
||||
|
@ -747,7 +758,6 @@ class Image(DbObject):
|
|||
if os.path.exists(pathname):
|
||||
raise RuntimeError("Image already exists")
|
||||
img = Image({'name': name, 'pathname': pathname, 'owner': user['name'], 'public': public})
|
||||
print("Image: add %s to fetch queue" % (url))
|
||||
acp_queue(url, pathname)
|
||||
return img
|
||||
|
||||
|
@ -785,7 +795,7 @@ class VirtualMachine(DbObject):
|
|||
def __init__(self, row):
|
||||
DbObject.__init__(self, row)
|
||||
self._lock = threading.Lock()
|
||||
if self['macaddr'] is None:
|
||||
if self.get('macaddr', None) is None:
|
||||
found = False
|
||||
while not found:
|
||||
b4 = int(random.random() * 256)
|
||||
|
@ -797,8 +807,15 @@ class VirtualMachine(DbObject):
|
|||
if not row:
|
||||
found = True
|
||||
self['macaddr'] = macaddr
|
||||
if self['vncpass'] is None:
|
||||
if self.get('vncpass', None) is None:
|
||||
self['vncpass'] = pwgen(8)
|
||||
if self.get('uuid', None) is None:
|
||||
f1 = "%08x" % (int(random.random() * (1 << 32)))
|
||||
f2 = "%04x" % (int(random.random() * (1 << 16)))
|
||||
f3 = "%04x" % (int(random.random() * (1 << 16)))
|
||||
f4 = "%04x" % (int(random.random() * (1 << 16)))
|
||||
f5 = "%012x" % (int(random.random() * (1 << 48)))
|
||||
self['uuid'] = "%s-%s-%s-%s-%s" % (f1, f2, f3, f4, f5)
|
||||
(self._disk_psize, self._disk_vsize, self._disk_fmt) = image_info(self['diskpath'])
|
||||
self._copy_status = None
|
||||
|
||||
|
@ -812,14 +829,14 @@ class VirtualMachine(DbObject):
|
|||
diskpath = VirtualMachine.pathname_for_disk(owner['name'], name, '.qcow2')
|
||||
if os.path.exists(diskpath):
|
||||
raise RuntimeError("Disk already exists")
|
||||
argv = [find_in_path('qemu-img'), 'create',
|
||||
argv = ['qemu-img', 'create',
|
||||
'-f', 'qcow2',
|
||||
'-o', 'preallocation=metadata',
|
||||
diskpath, "%dM" % (disk_size)]
|
||||
cmd_run(argv)
|
||||
return VirtualMachine({'name': name, 'owner': owner['name'],
|
||||
'arch': arch, 'cpus': cpus, 'mem': mem,
|
||||
'diskpath': diskpath, 'macaddr': None, 'vncpass': None})
|
||||
'diskpath': diskpath})
|
||||
|
||||
@staticmethod
|
||||
def create_from_image(name, owner, arch, cpus, mem, image_id):
|
||||
|
@ -830,20 +847,18 @@ class VirtualMachine(DbObject):
|
|||
if os.path.exists(diskpath):
|
||||
raise RuntimeError("Disk already exists")
|
||||
if ext == '.qcow2':
|
||||
argv = [find_in_path('qemu-img'), 'create', '-f', 'qcow2', '-b', img['pathname'], diskpath]
|
||||
argv = ['qemu-img', 'create', '-f', 'qcow2', '-b', img['pathname'], diskpath]
|
||||
cmd_run(argv)
|
||||
else:
|
||||
acp_queue(img['pathname'], diskpath)
|
||||
return VirtualMachine({'name': name, 'owner': owner['name'],
|
||||
'arch': arch, 'cpus': cpus, 'mem': mem,
|
||||
'vncpass': None, 'macaddr': None,
|
||||
'diskpath': diskpath})
|
||||
|
||||
@staticmethod
|
||||
def create_from_local(name, owner, arch, cpus, mem, pathname):
|
||||
return VirtualMachine({'name': name, 'owner': owner['name'],
|
||||
'arch': arch, 'cpus': cpus, 'mem': mem,
|
||||
'vncpass': None, 'macaddr': None,
|
||||
'diskpath': pathname})
|
||||
|
||||
@staticmethod
|
||||
|
@ -863,7 +878,7 @@ class VirtualMachine(DbObject):
|
|||
diskpath = VirtualMachine.pathname_for_disk(owner.name(), name, ext)
|
||||
if os.path.exists(diskpath):
|
||||
raise RuntimeError("Disk already exists")
|
||||
vm = VirtualMachine(None, name, owner, arch, cpus, mem, None, None, diskpath)
|
||||
vm = VirtualMachine.create_from_local(name, owner, arch, cpus, mem, diskpath)
|
||||
acp_queue(image_url, diskpath)
|
||||
return vm
|
||||
|
||||
|
@ -885,7 +900,7 @@ class VirtualMachine(DbObject):
|
|||
def _snapshot_list(self):
|
||||
snapshots = []
|
||||
if self['diskpath'].endswith('.qcow2'):
|
||||
argv = [find_in_path('qemu-img'), 'snapshot', '-l', self['diskpath']]
|
||||
argv = ['qemu-img', 'snapshot', '-l', self['diskpath']]
|
||||
(out, err) = cmd_run(argv)
|
||||
for line in out.rstrip('\n').split('\n'):
|
||||
fields = line.split()
|
||||
|
@ -933,7 +948,6 @@ class VirtualMachine(DbObject):
|
|||
return 'stopped'
|
||||
|
||||
def ipv4addr(self):
|
||||
print("mac=%s leases=%s" % (self['macaddr'], leases))
|
||||
return leases.get(self['macaddr'], None)
|
||||
|
||||
def ipv6addr(self, val=None):
|
||||
|
@ -972,8 +986,9 @@ class VirtualMachine(DbObject):
|
|||
cpu_arg = 'cortex-a53'
|
||||
else:
|
||||
raise RuntimeError('Unknown arch')
|
||||
argv = [find_in_path(prog)]
|
||||
argv = [prog]
|
||||
argv.extend(['-daemonize', '-pidfile', self._qemu_pidfile()])
|
||||
argv.extend(['-name', self['name']])
|
||||
argv.extend(['-machine', machine_arg, '-cpu', cpu_arg])
|
||||
ethdev = 'virtio-net' if self['ostype'] == 'linux' else 'e1000'
|
||||
blkopt = ''
|
||||
|
@ -988,6 +1003,7 @@ class VirtualMachine(DbObject):
|
|||
'-monitor', "unix:%s/monitor,server,nowait" % (vm_run_dir),
|
||||
'-serial', "unix:%s/serial,server,nowait" % (vm_run_dir),
|
||||
'-vnc', ":%d,password=on" % (self['id']),
|
||||
'-smbios', "type=1,uuid=%s" % (self['uuid']),
|
||||
'-drive', "file=%s%s" % (self['diskpath'], blkopt)])
|
||||
if has_pci:
|
||||
argv.extend(['-netdev', "bridge,br=%s,id=net1" % (config['network.bridge.name']),
|
||||
|
@ -1005,8 +1021,11 @@ class VirtualMachine(DbObject):
|
|||
pass
|
||||
fork_child(argv)
|
||||
file_wait_exists(self._qemu_pidfile(), 2.0)
|
||||
res = self._run_monitor_command('info status', 30.0)
|
||||
if not res:
|
||||
loge('Failed to communicate with monitor')
|
||||
if resuming:
|
||||
self._run_monitor_command('delvm vmm-suspend', 5.0)
|
||||
self._run_monitor_command('delvm vmm-suspend')
|
||||
if self['vncpass']:
|
||||
self._run_monitor_command("change vnc password %s" % (self['vncpass']))
|
||||
|
||||
|
@ -1825,15 +1844,25 @@ class HttpClientRequestHandler(http.server.BaseHTTPRequestHandler):
|
|||
def ui_vm(self, user, args):
|
||||
# XXX: auth
|
||||
r = self._html_head(user)
|
||||
if 'id' in args:
|
||||
err = None
|
||||
msg = None
|
||||
is_admin = user.in_group('admin')
|
||||
if 'id' in args:
|
||||
args_id = int(args['id'][0])
|
||||
row = vms_table.select_by_id(args_id)
|
||||
vm = VirtualMachine(row)
|
||||
if vm['owner'] != user['name'] and not is_admin:
|
||||
r += ' <p>Access denied</p>\n'
|
||||
r += self._html_foot(user)
|
||||
self._send_response(403, None, r)
|
||||
return
|
||||
else:
|
||||
args_id = None
|
||||
vm = None
|
||||
if vm:
|
||||
server_host = self.headers['Host']
|
||||
if ':' in server_host:
|
||||
server_host = server_host.split(':')[0]
|
||||
vm_id = int(args['id'][0])
|
||||
row = vms_table.select_by_id(vm_id)
|
||||
vm = VirtualMachine(row)
|
||||
vm_running = vm.running()
|
||||
edit_mode = (not vm_running) and ('action' in args) and (args['action'][0] == 'Edit')
|
||||
if 'action' in args:
|
||||
|
@ -1893,7 +1922,7 @@ class HttpClientRequestHandler(http.server.BaseHTTPRequestHandler):
|
|||
if err:
|
||||
r += " <p style=\"font-size:125%%;color:red\">%s</p>\n" % (err)
|
||||
r += ' <form method="POST" action="/ui/vm">\n'
|
||||
r += " <input type=\"hidden\" name=\"id\" value=\"%d\">\n" % (vm_id)
|
||||
r += " <input type=\"hidden\" name=\"id\" value=\"%d\">\n" % (args_id)
|
||||
r += ' <table>\n'
|
||||
if edit_mode:
|
||||
r += " <tr><td style=\"font-weight:bold\">Name<td><input type=\"text\" name=\"name\" value=\"%s\">\n" % (vm['name'])
|
||||
|
@ -1916,7 +1945,7 @@ class HttpClientRequestHandler(http.server.BaseHTTPRequestHandler):
|
|||
r += " <tr><td style=\"font-weight:bold\">Addr<td>%s\n" % (vm.ipv4addr())
|
||||
r += ' <tr><td> <td> \n'
|
||||
if vm_running:
|
||||
r += " <tr><td style=\"font-weight:bold\">VNC Host<td>%s:%d\n" % (server_host, vm_id)
|
||||
r += " <tr><td style=\"font-weight:bold\">VNC Host<td>%s:%d\n" % (server_host, args_id)
|
||||
r += " <tr><td style=\"font-weight:bold\">VNC Pass<td>%s\n" % (vm['vncpass'])
|
||||
r += ' <tr><td> <td> \n'
|
||||
r += ' </table>\n'
|
||||
|
@ -1944,7 +1973,7 @@ class HttpClientRequestHandler(http.server.BaseHTTPRequestHandler):
|
|||
r += '<input type="checkbox" name="readonly">Read only mode\n'
|
||||
r += ' <tr><td colspan="2"><input style="color:red" type="submit" name="action" value="Delete">\n'
|
||||
r += ' <tr><td colspan="2"><hr>\n'
|
||||
r += ' <tr><td style="font-size:125%">Create image from disk<td> \n'
|
||||
r += ' <tr><td colspan="2" style="font-size:125%">Create image from disk\n'
|
||||
r += ' <tr><td style="font-weight:bold">Name<td><input type="text" name="image_name">\n'
|
||||
r += ' <tr><td> <td><input type="checkbox" name="image_public">Public\n'
|
||||
r += ' <tr><td colspan="2"><input type="submit" name="action" value="Create Image">\n'
|
||||
|
@ -1989,7 +2018,6 @@ class HttpClientRequestHandler(http.server.BaseHTTPRequestHandler):
|
|||
msg = None
|
||||
err = None
|
||||
if 'action' in args and args['action'][0] == 'Create':
|
||||
print("Creating VM")
|
||||
vm = None
|
||||
name = args['name'][0]
|
||||
arch = args['arch'][0]
|
||||
|
@ -2239,7 +2267,6 @@ def acp_simple(srcfile, dstfile, file_size):
|
|||
off += len(buf)
|
||||
curpct = int(100 * off / file_size)
|
||||
if curpct != pct:
|
||||
print("acp_simple: dstfile=%s curpct=%d" % (dstfile, curpct))
|
||||
pct = curpct
|
||||
_acp_lock.acquire()
|
||||
_acp_pct = pct
|
||||
|
@ -2331,7 +2358,6 @@ def lease_updater():
|
|||
new_leases[macaddr] = ipv4addr
|
||||
f.close()
|
||||
leases = new_leases
|
||||
print("leases=%s" % (leases))
|
||||
except OSError as e:
|
||||
last_mtime = 0
|
||||
except BaseException as e:
|
||||
|
@ -2440,6 +2466,7 @@ vms_table = DbTable(dbconn, 'vms',
|
|||
mem INTEGER,
|
||||
ostype VARCHAR(64),
|
||||
vncpass CHAR(8),
|
||||
uuid CHAR(36),
|
||||
macaddr CHAR(17),
|
||||
disksize INTEGER,
|
||||
diskpath VARCHAR(256),
|
||||
|
@ -2490,23 +2517,20 @@ if vms_table.empty():
|
|||
# Setup networking
|
||||
|
||||
if config['network.mode'] == 'bridge':
|
||||
argv = [find_in_path('brctl'),
|
||||
'addbr', config['network.bridge.name']]
|
||||
argv = ['brctl', 'addbr', config['network.bridge.name']]
|
||||
try:
|
||||
cmd_run(argv)
|
||||
except:
|
||||
# XXX: handle errors other than already exists
|
||||
pass
|
||||
argv = [find_in_path('ip'),
|
||||
'addr', 'add', config['network.bridge.addr'],
|
||||
argv = ['ip', 'addr', 'add', config['network.bridge.addr'],
|
||||
'dev', config['network.bridge.name']]
|
||||
try:
|
||||
cmd_run(argv)
|
||||
except:
|
||||
# XXX: handle errors other than already exists
|
||||
pass
|
||||
argv = [find_in_path('ip'),
|
||||
'link', 'set', config['network.bridge.name'], 'up']
|
||||
argv = ['ip', 'link', 'set', config['network.bridge.name'], 'up']
|
||||
try:
|
||||
cmd_run(argv)
|
||||
except:
|
||||
|
@ -2533,8 +2557,7 @@ if config['network.mode'] == 'bridge':
|
|||
f.write("dhcp-range=%s,%s\n" % (config['network.dhcp.start'], config['network.dhcp.end']))
|
||||
f.write("dhcp-authoritative\n")
|
||||
f.close()
|
||||
args = []
|
||||
args.append(find_in_path('dnsmasq'))
|
||||
args = ['dnsmasq']
|
||||
args.append("--conf-file=%s" % (cfg_filename))
|
||||
fork_child(args)
|
||||
|
||||
|
@ -2555,6 +2578,7 @@ for entry in os.listdir(run_dir):
|
|||
|
||||
signal.signal(signal.SIGTERM, sig_term)
|
||||
signal.signal(signal.SIGINT, sig_term)
|
||||
signal.signal(signal.SIGCHLD, sig_chld)
|
||||
|
||||
file_copier_thread = threading.Thread(target=file_copier)
|
||||
file_copier_thread.daemon = True
|
||||
|
|
Loading…
Reference in New Issue