2010-04-05 20:58:57 +02:00
|
|
|
#!/usr/bin/python2.6
|
|
|
|
#
|
|
|
|
# The Qubes OS Project, http://www.qubes-os.org
|
|
|
|
#
|
|
|
|
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU General Public License
|
|
|
|
# as published by the Free Software Foundation; either version 2
|
|
|
|
# of the License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
#
|
|
|
|
#
|
|
|
|
|
|
|
|
import sys
|
2012-02-24 04:23:27 +01:00
|
|
|
import stat
|
2010-04-05 20:58:57 +02:00
|
|
|
import os
|
|
|
|
import os.path
|
|
|
|
import subprocess
|
|
|
|
import xml.etree.ElementTree
|
|
|
|
import xml.parsers.expat
|
|
|
|
import fcntl
|
|
|
|
import re
|
|
|
|
import shutil
|
2011-06-01 23:44:06 +02:00
|
|
|
import uuid
|
|
|
|
import time
|
2011-03-02 15:03:21 +01:00
|
|
|
from datetime import datetime
|
2010-08-30 11:40:19 +02:00
|
|
|
from qmemman_client import QMemmanClient
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
# Do not use XenAPI or create/read any VM files
|
|
|
|
# This is for testing only!
|
|
|
|
dry_run = False
|
|
|
|
#dry_run = True
|
|
|
|
|
|
|
|
|
|
|
|
if not dry_run:
|
2011-06-01 23:44:06 +02:00
|
|
|
import xen.lowlevel.xc
|
|
|
|
import xen.lowlevel.xl
|
|
|
|
import xen.lowlevel.xs
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2010-05-11 14:48:24 +02:00
|
|
|
qubes_guid_path = "/usr/bin/qubes_guid"
|
2011-03-31 11:11:39 +02:00
|
|
|
qrexec_daemon_path = "/usr/lib/qubes/qrexec_daemon"
|
2011-05-20 16:36:48 +02:00
|
|
|
qrexec_client_path = "/usr/lib/qubes/qrexec_client"
|
2011-10-31 13:31:17 +01:00
|
|
|
qubes_clipd_path = "/usr/bin/qclipd"
|
2010-05-11 14:48:24 +02:00
|
|
|
|
2010-06-26 15:02:58 +02:00
|
|
|
qubes_base_dir = "/var/lib/qubes"
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-03-02 15:00:19 +01:00
|
|
|
qubes_appvms_dir = qubes_base_dir + "/appvms"
|
|
|
|
qubes_templates_dir = qubes_base_dir + "/vm-templates"
|
|
|
|
qubes_servicevms_dir = qubes_base_dir + "/servicevms"
|
2010-04-05 20:58:57 +02:00
|
|
|
qubes_store_filename = qubes_base_dir + "/qubes.xml"
|
2011-06-30 01:07:47 +02:00
|
|
|
qubes_kernels_base_dir = qubes_base_dir + "/vm-kernels"
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
qubes_max_xid = 1024
|
2011-03-27 12:28:13 +02:00
|
|
|
qubes_max_qid = 254
|
2010-04-05 20:58:57 +02:00
|
|
|
qubes_max_netid = 254
|
2011-03-27 12:28:13 +02:00
|
|
|
vm_default_netmask = "255.255.255.0"
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
default_root_img = "root.img"
|
|
|
|
default_rootcow_img = "root-cow.img"
|
2011-03-19 03:15:32 +01:00
|
|
|
default_volatile_img = "volatile.img"
|
2011-03-19 22:05:00 +01:00
|
|
|
default_clean_volatile_img = "clean-volatile.img.tar"
|
2010-04-05 20:58:57 +02:00
|
|
|
default_private_img = "private.img"
|
|
|
|
default_appmenus_templates_subdir = "apps.templates"
|
2011-04-19 16:09:11 +02:00
|
|
|
default_appmenus_template_templates_subdir = "apps-template.templates"
|
2010-04-05 20:58:57 +02:00
|
|
|
default_kernels_subdir = "kernels"
|
2011-02-21 18:13:27 +01:00
|
|
|
default_firewall_conf_file = "firewall.xml"
|
2011-03-16 18:39:54 +01:00
|
|
|
default_memory = 400
|
|
|
|
default_servicevm_vcpus = 1
|
2011-08-31 20:34:56 +02:00
|
|
|
default_kernelopts = ""
|
2011-09-08 14:09:03 +02:00
|
|
|
default_kernelopts_pcidevs = "iommu=soft swiotlb=2048"
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2012-02-24 04:23:27 +01:00
|
|
|
default_hvm_disk_size = 20*1024*1024*1024
|
2012-03-02 14:28:09 +01:00
|
|
|
default_hvm_memory = 512
|
2012-02-24 04:23:27 +01:00
|
|
|
|
2012-02-24 04:17:45 +01:00
|
|
|
config_template_pv = '/usr/share/qubes/vm-template.conf'
|
2012-02-24 04:23:27 +01:00
|
|
|
config_template_hvm = '/usr/share/qubes/vm-template-hvm.conf'
|
2012-02-24 04:17:45 +01:00
|
|
|
|
2012-03-02 01:46:10 +01:00
|
|
|
start_appmenu_template = '/usr/share/qubes/qubes-start.desktop'
|
|
|
|
|
2011-07-05 21:20:43 +02:00
|
|
|
qubes_whitelisted_appmenus = 'whitelisted-appmenus.list'
|
|
|
|
|
2011-06-22 00:44:48 +02:00
|
|
|
dom0_update_check_interval = 6*3600
|
|
|
|
|
2011-10-17 23:08:53 +02:00
|
|
|
# how long (in sec) to wait for VMs to shutdown
|
|
|
|
# before killing them (when used qvm-run with --wait option)
|
2011-10-17 23:09:14 +02:00
|
|
|
shutdown_counter_max = 60
|
2011-10-17 23:08:53 +02:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
# do not allow to start a new AppVM if Dom0 mem was to be less than this
|
|
|
|
dom0_min_memory = 700*1024*1024
|
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
# We need this global reference, as each instance of QubesVm
|
2010-04-05 20:58:57 +02:00
|
|
|
# must be able to ask Dom0 VM about how much memory it currently has...
|
|
|
|
dom0_vm = None
|
|
|
|
|
|
|
|
qubes_appmenu_create_cmd = "/usr/lib/qubes/create_apps_for_appvm.sh"
|
|
|
|
qubes_appmenu_remove_cmd = "/usr/lib/qubes/remove_appvm_appmenus.sh"
|
2011-07-05 22:10:09 +02:00
|
|
|
qubes_pciback_cmd = '/usr/lib/qubes/unbind_pci_device.sh'
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
class QubesException (Exception) : pass
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
if not dry_run:
|
|
|
|
xc = xen.lowlevel.xc.xc()
|
|
|
|
xs = xen.lowlevel.xs.xs()
|
|
|
|
xl_ctx = xen.lowlevel.xl.ctx()
|
2010-09-16 17:52:52 +02:00
|
|
|
|
|
|
|
class QubesHost(object):
|
|
|
|
def __init__(self):
|
2011-06-01 23:44:06 +02:00
|
|
|
self.physinfo = xc.physinfo()
|
2010-09-16 17:52:52 +02:00
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
self.xen_total_mem = long(self.physinfo['total_memory'])
|
|
|
|
self.xen_no_cpus = self.physinfo['nr_cpus']
|
2010-09-16 17:52:52 +02:00
|
|
|
|
|
|
|
# print "QubesHost: total_mem = {0}B".format (self.xen_total_mem)
|
|
|
|
# print "QubesHost: free_mem = {0}".format (self.get_free_xen_memory())
|
|
|
|
# print "QubesHost: total_cpus = {0}".format (self.xen_no_cpus)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def memory_total(self):
|
|
|
|
return self.xen_total_mem
|
|
|
|
|
|
|
|
@property
|
|
|
|
def no_cpus(self):
|
|
|
|
return self.xen_no_cpus
|
|
|
|
|
|
|
|
def get_free_xen_memory(self):
|
2011-06-01 23:44:06 +02:00
|
|
|
ret = self.physinfo['free_memory']
|
2010-09-16 17:52:52 +02:00
|
|
|
return long(ret)
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
# measure cpu usage for all domains at once
|
|
|
|
def measure_cpu_usage(self, previous=None, previous_time = None, wait_time=1):
|
|
|
|
if previous is None:
|
|
|
|
previous_time = time.time()
|
|
|
|
previous = {}
|
|
|
|
info = xc.domain_getinfo(0, qubes_max_xid)
|
|
|
|
for vm in info:
|
|
|
|
previous[vm['domid']] = {}
|
|
|
|
previous[vm['domid']]['cpu_time'] = vm['cpu_time']/vm['online_vcpus']
|
|
|
|
previous[vm['domid']]['cpu_usage'] = 0
|
|
|
|
time.sleep(wait_time)
|
|
|
|
|
|
|
|
current_time = time.time()
|
|
|
|
current = {}
|
|
|
|
info = xc.domain_getinfo(0, qubes_max_xid)
|
|
|
|
for vm in info:
|
|
|
|
current[vm['domid']] = {}
|
2011-06-04 02:44:27 +02:00
|
|
|
current[vm['domid']]['cpu_time'] = vm['cpu_time']/max(vm['online_vcpus'],1)
|
2011-06-01 23:44:06 +02:00
|
|
|
if vm['domid'] in previous.keys():
|
|
|
|
current[vm['domid']]['cpu_usage'] = \
|
|
|
|
float(current[vm['domid']]['cpu_time'] - previous[vm['domid']]['cpu_time']) \
|
|
|
|
/ long(1000**3) / (current_time-previous_time) * 100
|
2011-06-11 20:44:26 +02:00
|
|
|
if current[vm['domid']]['cpu_usage'] < 0:
|
|
|
|
# VM has been rebooted
|
|
|
|
current[vm['domid']]['cpu_usage'] = 0
|
2011-06-01 23:44:06 +02:00
|
|
|
else:
|
|
|
|
current[vm['domid']]['cpu_usage'] = 0
|
|
|
|
|
|
|
|
return (current_time, current)
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
class QubesVmLabel(object):
|
2010-05-10 15:01:47 +02:00
|
|
|
def __init__(self, name, index, color = None, icon = None):
|
2010-04-05 20:58:57 +02:00
|
|
|
self.name = name
|
2010-05-10 15:01:47 +02:00
|
|
|
self.index = index
|
2010-04-05 20:58:57 +02:00
|
|
|
self.color = color if color is not None else name
|
|
|
|
self.icon = icon if icon is not None else name
|
|
|
|
self.icon_path = "/usr/share/qubes/icons/" + self.icon + ".png"
|
|
|
|
|
|
|
|
# Globally defined lables
|
|
|
|
QubesVmLabels = {
|
2010-06-03 23:04:06 +02:00
|
|
|
"red" : QubesVmLabel ("red", 1),
|
|
|
|
"orange" : QubesVmLabel ("orange", 2),
|
|
|
|
"yellow" : QubesVmLabel ("yellow", 3),
|
|
|
|
"green" : QubesVmLabel ("green", 4, color="0x5fa05e"),
|
|
|
|
"gray" : QubesVmLabel ("gray", 5),
|
|
|
|
"blue" : QubesVmLabel ("blue", 6),
|
|
|
|
"purple" : QubesVmLabel ("purple", 7, color="0xb83374"),
|
|
|
|
"black" : QubesVmLabel ("black", 8),
|
2010-04-05 20:58:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
default_appvm_label = QubesVmLabels["red"]
|
|
|
|
default_template_label = QubesVmLabels["gray"]
|
|
|
|
default_servicevm_label = QubesVmLabels["red"]
|
|
|
|
|
|
|
|
class QubesVm(object):
|
|
|
|
"""
|
|
|
|
A representation of one Qubes VM
|
|
|
|
Only persistent information are stored here, while all the runtime
|
|
|
|
information, e.g. Xen dom id, etc, are to be retrieved via Xen API
|
|
|
|
Note that qid is not the same as Xen's domid!
|
|
|
|
"""
|
|
|
|
|
2011-02-09 21:35:36 +01:00
|
|
|
def __init__(self, qid, name,
|
2010-04-05 20:58:57 +02:00
|
|
|
dir_path, conf_file = None,
|
|
|
|
uses_default_netvm = True,
|
2012-03-04 21:59:02 +01:00
|
|
|
netvm = None,
|
2010-04-05 20:58:57 +02:00
|
|
|
installed_by_rpm = False,
|
|
|
|
updateable = False,
|
2011-03-16 18:39:54 +01:00
|
|
|
label = None,
|
2011-03-16 23:45:02 +01:00
|
|
|
root_img = None,
|
|
|
|
private_img = None,
|
2011-03-16 18:39:54 +01:00
|
|
|
memory = default_memory,
|
2011-04-29 01:38:43 +02:00
|
|
|
maxmem = None,
|
2011-04-01 01:14:18 +02:00
|
|
|
template_vm = None,
|
|
|
|
firewall_conf = None,
|
|
|
|
volatile_img = None,
|
2011-04-02 00:37:38 +02:00
|
|
|
pcidevs = None,
|
2011-04-03 17:47:20 +02:00
|
|
|
internal = False,
|
2011-06-30 01:07:47 +02:00
|
|
|
vcpus = None,
|
|
|
|
kernel = None,
|
2011-08-31 20:34:56 +02:00
|
|
|
uses_default_kernel = True,
|
|
|
|
kernelopts = "",
|
2011-10-01 02:49:25 +02:00
|
|
|
uses_default_kernelopts = True,
|
2012-03-02 16:07:31 +01:00
|
|
|
mac = None,
|
2011-10-01 02:49:25 +02:00
|
|
|
services = None):
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
assert qid < qubes_max_qid, "VM id out of bounds!"
|
|
|
|
self.__qid = qid
|
|
|
|
self.name = name
|
|
|
|
|
|
|
|
self.dir_path = dir_path
|
2011-11-01 23:31:37 +01:00
|
|
|
|
|
|
|
self.conf_file = self.absolute_path(conf_file, name + ".conf")
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
self.uses_default_netvm = uses_default_netvm
|
2012-03-04 21:59:02 +01:00
|
|
|
self.netvm = netvm
|
|
|
|
if netvm is not None:
|
|
|
|
netvm.connected_vms[qid] = self
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2012-03-02 16:07:31 +01:00
|
|
|
self._mac = mac
|
|
|
|
|
2011-03-11 01:38:04 +01:00
|
|
|
# We use it in remove from disk to avoid removing rpm files (for templates)
|
2010-04-05 20:58:57 +02:00
|
|
|
self.installed_by_rpm = installed_by_rpm
|
|
|
|
|
2011-03-16 23:45:02 +01:00
|
|
|
# Setup standard VM storage; some VM types may not use them all
|
2011-11-01 23:31:37 +01:00
|
|
|
self.root_img = self.absolute_path(root_img, default_root_img)
|
2011-03-16 23:45:02 +01:00
|
|
|
|
2011-11-01 23:31:37 +01:00
|
|
|
self.volatile_img = self.absolute_path(volatile_img, default_volatile_img)
|
2011-03-16 23:45:02 +01:00
|
|
|
|
2011-11-01 23:31:37 +01:00
|
|
|
self.private_img = self.absolute_path(private_img, default_private_img)
|
2011-03-16 23:45:02 +01:00
|
|
|
|
2011-11-01 23:31:37 +01:00
|
|
|
self.firewall_conf = self.absolute_path(firewall_conf, default_firewall_conf_file)
|
2011-04-01 01:14:18 +02:00
|
|
|
|
2012-02-24 04:17:45 +01:00
|
|
|
self.config_file_template = config_template_pv
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
self.updateable = updateable
|
2012-02-22 15:14:00 +01:00
|
|
|
self._label = label if label is not None else QubesVmLabels["red"]
|
2010-09-21 15:59:22 +02:00
|
|
|
if self.dir_path is not None:
|
|
|
|
self.icon_path = self.dir_path + "/icon.png"
|
|
|
|
else:
|
|
|
|
self.icon_path = None
|
2011-03-02 15:00:19 +01:00
|
|
|
|
2011-03-11 01:48:27 +01:00
|
|
|
# PCI devices - used only by NetVM
|
2011-04-02 00:37:38 +02:00
|
|
|
if pcidevs is None or pcidevs == "none":
|
2011-07-05 22:01:28 +02:00
|
|
|
self.pcidevs = []
|
|
|
|
elif pcidevs.find('[') < 0:
|
|
|
|
# Backward compatibility
|
|
|
|
self.pcidevs = eval('[' + pcidevs + ']')
|
2011-04-02 00:37:38 +02:00
|
|
|
else:
|
2011-07-05 22:01:28 +02:00
|
|
|
self.pcidevs = eval(pcidevs)
|
2011-03-11 01:48:27 +01:00
|
|
|
|
2011-03-16 18:39:54 +01:00
|
|
|
self.memory = memory
|
|
|
|
|
2011-04-29 01:38:43 +02:00
|
|
|
if maxmem is None:
|
2011-06-01 23:44:06 +02:00
|
|
|
host = QubesHost()
|
|
|
|
total_mem_mb = host.memory_total/1024
|
2011-04-29 01:38:43 +02:00
|
|
|
self.maxmem = total_mem_mb/2
|
|
|
|
else:
|
|
|
|
self.maxmem = maxmem
|
|
|
|
|
2011-04-01 02:06:22 +02:00
|
|
|
self.template_vm = template_vm
|
2011-04-01 01:14:18 +02:00
|
|
|
if template_vm is not None:
|
|
|
|
if updateable:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "ERROR: Template based VM cannot be updateable!"
|
2011-04-01 02:06:22 +02:00
|
|
|
return False
|
2011-04-01 01:14:18 +02:00
|
|
|
if not template_vm.is_template():
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\
|
2011-04-01 01:14:18 +02:00
|
|
|
format(template_vm.qid)
|
|
|
|
return False
|
|
|
|
|
|
|
|
template_vm.appvms[qid] = self
|
|
|
|
else:
|
|
|
|
assert self.root_img is not None, "Missing root_img for standalone VM!"
|
|
|
|
|
2011-06-30 01:07:47 +02:00
|
|
|
self.kernel = kernel
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
if template_vm is not None:
|
|
|
|
self.kernels_dir = template_vm.kernels_dir
|
2011-08-31 18:32:37 +02:00
|
|
|
self.kernel = template_vm.kernel
|
2011-06-30 01:07:47 +02:00
|
|
|
elif self.kernel is not None:
|
|
|
|
self.kernels_dir = qubes_kernels_base_dir + "/" + self.kernel
|
2011-06-01 23:44:06 +02:00
|
|
|
else:
|
2011-06-30 01:07:47 +02:00
|
|
|
# for backward compatibility (or another rare case): kernel=None -> kernel in VM dir
|
2011-06-01 23:44:06 +02:00
|
|
|
self.kernels_dir = self.dir_path + "/" + default_kernels_subdir
|
|
|
|
|
2011-06-30 01:07:47 +02:00
|
|
|
self.uses_default_kernel = uses_default_kernel
|
|
|
|
|
2011-07-05 21:26:39 +02:00
|
|
|
self.appmenus_templates_dir = None
|
2011-05-24 00:14:03 +02:00
|
|
|
if updateable:
|
|
|
|
self.appmenus_templates_dir = self.dir_path + "/" + default_appmenus_templates_subdir
|
2011-07-09 00:33:48 +02:00
|
|
|
elif template_vm is not None:
|
2011-07-05 21:26:39 +02:00
|
|
|
self.appmenus_templates_dir = template_vm.appmenus_templates_dir
|
2011-05-24 00:14:03 +02:00
|
|
|
|
2011-03-16 18:39:54 +01:00
|
|
|
# By default allow use all VCPUs
|
|
|
|
if vcpus is None:
|
|
|
|
qubes_host = QubesHost()
|
|
|
|
self.vcpus = qubes_host.no_cpus
|
|
|
|
else:
|
|
|
|
self.vcpus = vcpus
|
|
|
|
|
2011-08-31 20:34:56 +02:00
|
|
|
self.uses_default_kernelopts = uses_default_kernelopts
|
|
|
|
if self.uses_default_kernelopts:
|
|
|
|
if len(self.pcidevs) > 0:
|
|
|
|
self.kernelopts = default_kernelopts_pcidevs
|
|
|
|
else:
|
|
|
|
self.kernelopts = default_kernelopts
|
|
|
|
else:
|
|
|
|
self.kernelopts = kernelopts
|
|
|
|
|
2011-10-01 02:49:25 +02:00
|
|
|
self.services = {}
|
|
|
|
if services is not None:
|
|
|
|
self.services = eval(str(services))
|
|
|
|
|
2011-04-03 17:47:20 +02:00
|
|
|
# Internal VM (not shown in qubes-manager, doesn't create appmenus entries
|
|
|
|
self.internal = internal
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
self.xid = -1
|
|
|
|
self.xid = self.get_xid()
|
2010-05-07 15:42:34 +02:00
|
|
|
|
2011-11-01 23:31:37 +01:00
|
|
|
def absolute_path(self, arg, default):
|
|
|
|
if arg is not None and os.path.isabs(arg):
|
|
|
|
return arg
|
|
|
|
else:
|
|
|
|
return self.dir_path + "/" + (arg if arg is not None else default)
|
|
|
|
|
|
|
|
def relative_path(self, arg):
|
|
|
|
return arg.replace(self.dir_path + '/', '')
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
@property
|
|
|
|
def qid(self):
|
|
|
|
return self.__qid
|
|
|
|
|
2012-02-22 15:14:00 +01:00
|
|
|
@property
|
|
|
|
def label(self):
|
|
|
|
return self._label
|
|
|
|
|
|
|
|
@label.setter
|
|
|
|
def label(self, new_label):
|
|
|
|
self._label = new_label
|
|
|
|
if self.icon_path:
|
|
|
|
try:
|
|
|
|
os.remove(self.icon_path)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
os.symlink (new_label.icon_path, self.icon_path)
|
2012-02-26 00:07:06 +01:00
|
|
|
subprocess.call(['sudo', 'xdg-icon-resource', 'forceupdate'])
|
2012-02-22 15:14:00 +01:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
@property
|
|
|
|
def ip(self):
|
2012-03-04 21:59:02 +01:00
|
|
|
if self.netvm is not None:
|
|
|
|
return self.netvm.get_ip_for_vm(self.qid)
|
2010-04-05 20:58:57 +02:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
@property
|
|
|
|
def netmask(self):
|
2012-03-04 21:59:02 +01:00
|
|
|
if self.netvm is not None:
|
|
|
|
return self.netvm.netmask
|
2010-04-05 20:58:57 +02:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
@property
|
|
|
|
def gateway(self):
|
2012-03-04 21:59:02 +01:00
|
|
|
if self.netvm is not None:
|
|
|
|
return self.netvm.gateway
|
2010-04-05 20:58:57 +02:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
2010-05-19 16:19:01 +02:00
|
|
|
@property
|
|
|
|
def secondary_dns(self):
|
2012-03-04 21:59:02 +01:00
|
|
|
if self.netvm is not None:
|
|
|
|
return self.netvm.secondary_dns
|
2010-05-19 16:19:01 +02:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
2012-03-02 01:16:03 +01:00
|
|
|
@property
|
|
|
|
def vif(self):
|
2012-03-02 02:26:37 +01:00
|
|
|
if self.xid < 0:
|
2012-03-02 01:16:03 +01:00
|
|
|
return None
|
2012-03-04 21:59:02 +01:00
|
|
|
if self.netvm is None:
|
2012-03-02 01:16:03 +01:00
|
|
|
return None
|
|
|
|
return "vif{0}.+".format(self.xid)
|
|
|
|
|
2012-03-02 16:07:31 +01:00
|
|
|
@property
|
|
|
|
def mac(self):
|
|
|
|
if self._mac is not None:
|
|
|
|
return self._mac
|
|
|
|
else:
|
|
|
|
return "00:16:3E:5E:6C:{qid:02X}".format(qid=self.qid)
|
|
|
|
|
|
|
|
@mac.setter
|
|
|
|
def mac(self, new_mac):
|
|
|
|
self._mac = new_mac
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def is_updateable(self):
|
|
|
|
return self.updateable
|
|
|
|
|
|
|
|
def is_networked(self):
|
|
|
|
if self.is_netvm():
|
|
|
|
return True
|
2011-03-02 15:00:19 +01:00
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
if self.netvm is not None:
|
2010-04-05 20:58:57 +02:00
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2011-04-01 01:17:18 +02:00
|
|
|
def set_updateable(self):
|
|
|
|
if self.is_updateable():
|
|
|
|
return
|
|
|
|
|
|
|
|
raise QubesException ("Change 'updateable' flag is not supported. Please use qvm-create.")
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
def set_nonupdateable(self):
|
|
|
|
if not self.is_updateable():
|
|
|
|
return
|
|
|
|
|
2011-04-01 01:17:18 +02:00
|
|
|
raise QubesException ("Change 'updateable' flag is not supported. Please use qvm-create.")
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-11-01 15:50:03 +01:00
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
def set_netvm(self, netvm):
|
|
|
|
if self.netvm is not None:
|
|
|
|
self.netvm.connected_vms.pop(self.qid)
|
2011-11-01 15:50:03 +01:00
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
if netvm is None:
|
2011-11-01 15:50:03 +01:00
|
|
|
# Set also firewall to block all traffic as discussed in #370
|
|
|
|
if os.path.exists(self.firewall_conf):
|
|
|
|
shutil.copy(self.firewall_conf, "%s/backup/%s-firewall-%s.xml"
|
|
|
|
% (qubes_base_dir, self.name, time.strftime('%Y-%m-%d-%H:%M:%S')))
|
|
|
|
self.write_firewall_conf({'allow': False, 'allowDns': False,
|
|
|
|
'allowIcmp': False, 'rules': []})
|
|
|
|
else:
|
2012-03-04 21:59:02 +01:00
|
|
|
netvm.connected_vms[self.qid]=self
|
2011-11-01 15:50:03 +01:00
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
self.netvm = netvm
|
2011-11-01 15:50:03 +01:00
|
|
|
|
2011-11-02 00:39:23 +01:00
|
|
|
def pre_rename(self, new_name):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def set_name(self, name):
|
|
|
|
if self.is_running():
|
|
|
|
raise QubesException("Cannot change name of running VM!")
|
|
|
|
|
|
|
|
self.pre_rename(name)
|
|
|
|
|
|
|
|
new_conf = "%s/%s.conf" % (self.dir_path, name)
|
|
|
|
if os.path.exists(self.conf_file):
|
|
|
|
os.rename(self.conf_file, "%s/%s.conf" % (self.dir_path, name))
|
|
|
|
old_dirpath = self.dir_path
|
|
|
|
new_dirpath = os.path.dirname(self.dir_path) + '/' + name
|
|
|
|
os.rename(old_dirpath, new_dirpath)
|
|
|
|
self.dir_path = new_dirpath
|
|
|
|
old_name = self.name
|
|
|
|
self.name = name
|
|
|
|
if self.private_img is not None:
|
|
|
|
self.private_img = self.private_img.replace(old_dirpath, new_dirpath)
|
|
|
|
if self.root_img is not None:
|
|
|
|
self.root_img = self.root_img.replace(old_dirpath, new_dirpath)
|
|
|
|
if self.volatile_img is not None:
|
|
|
|
self.volatile_img = self.volatile_img.replace(old_dirpath, new_dirpath)
|
|
|
|
if self.conf_file is not None:
|
|
|
|
self.conf_file = new_conf.replace(old_dirpath, new_dirpath)
|
|
|
|
if self.appmenus_templates_dir is not None:
|
|
|
|
self.appmenus_templates_dir = self.appmenus_templates_dir.replace(old_dirpath, new_dirpath)
|
|
|
|
if self.icon_path is not None:
|
|
|
|
self.icon_path = self.icon_path.replace(old_dirpath, new_dirpath)
|
|
|
|
if self.kernels_dir is not None:
|
|
|
|
self.kernels_dir = self.kernels_dir.replace(old_dirpath, new_dirpath)
|
|
|
|
|
|
|
|
self.post_rename(old_name)
|
|
|
|
|
|
|
|
def post_rename(self, old_name):
|
|
|
|
pass
|
|
|
|
|
2011-03-05 15:13:31 +01:00
|
|
|
def is_template(self):
|
2011-02-09 21:35:36 +01:00
|
|
|
return isinstance(self, QubesTemplateVm)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
def is_appvm(self):
|
2011-02-09 21:35:36 +01:00
|
|
|
return isinstance(self, QubesAppVm)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
def is_netvm(self):
|
2011-02-09 21:35:36 +01:00
|
|
|
return isinstance(self, QubesNetVm)
|
2011-02-09 21:21:14 +01:00
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
def is_proxyvm(self):
|
|
|
|
return isinstance(self, QubesProxyVm)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2010-09-21 15:59:22 +02:00
|
|
|
def is_disposablevm(self):
|
2011-02-09 21:35:36 +01:00
|
|
|
return isinstance(self, QubesDisposableVm)
|
2010-09-21 15:59:22 +02:00
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
def get_xl_dominfo(self):
|
2010-04-05 20:58:57 +02:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
domains = xl_ctx.list_domains()
|
|
|
|
for dominfo in domains:
|
|
|
|
domname = xl_ctx.domid_to_name(dominfo.domid)
|
|
|
|
if domname == self.name:
|
|
|
|
return dominfo
|
|
|
|
return None
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
def get_xc_dominfo(self):
|
2010-04-05 20:58:57 +02:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
start_xid = self.xid
|
|
|
|
if start_xid < 0:
|
|
|
|
start_xid = 0
|
2011-06-04 02:46:12 +02:00
|
|
|
try:
|
|
|
|
domains = xc.domain_getinfo(start_xid, qubes_max_xid-start_xid)
|
|
|
|
except xen.lowlevel.xc.Error:
|
|
|
|
return None
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
for dominfo in domains:
|
|
|
|
domname = xl_ctx.domid_to_name(dominfo['domid'])
|
|
|
|
if domname == self.name:
|
|
|
|
return dominfo
|
|
|
|
return None
|
2010-05-07 15:42:34 +02:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def get_xid(self):
|
|
|
|
if dry_run:
|
|
|
|
return 666
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
dominfo = self.get_xc_dominfo()
|
|
|
|
if dominfo:
|
2011-06-02 00:07:22 +02:00
|
|
|
self.xid = dominfo['domid']
|
|
|
|
return self.xid
|
2011-06-01 23:44:06 +02:00
|
|
|
else:
|
|
|
|
return -1
|
2010-05-08 16:28:13 +02:00
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
def get_uuid(self):
|
|
|
|
|
|
|
|
dominfo = self.get_xl_dominfo()
|
|
|
|
if dominfo:
|
2011-07-20 16:12:28 +02:00
|
|
|
vmuuid = uuid.UUID(''.join('%02x' % b for b in dominfo.uuid))
|
|
|
|
return vmuuid
|
2011-06-01 23:44:06 +02:00
|
|
|
else:
|
|
|
|
return None
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
def get_mem(self):
|
|
|
|
if dry_run:
|
|
|
|
return 666
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
dominfo = self.get_xc_dominfo()
|
|
|
|
if dominfo:
|
|
|
|
return dominfo['mem_kb']
|
|
|
|
else:
|
|
|
|
return 0
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
def get_mem_static_max(self):
|
|
|
|
if dry_run:
|
|
|
|
return 666
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
dominfo = self.get_xc_dominfo()
|
|
|
|
if dominfo:
|
|
|
|
return dominfo['maxmem_kb']
|
|
|
|
else:
|
|
|
|
return 0
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
def get_per_cpu_time(self):
|
2010-04-05 20:58:57 +02:00
|
|
|
if dry_run:
|
|
|
|
import random
|
|
|
|
return random.random() * 100
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
dominfo = self.get_xc_dominfo()
|
|
|
|
if dominfo:
|
|
|
|
return dominfo['cpu_time']/dominfo['online_vcpus']
|
|
|
|
else:
|
2010-04-05 20:58:57 +02:00
|
|
|
return 0
|
|
|
|
|
2011-03-16 23:45:02 +01:00
|
|
|
def get_disk_utilization_root_img(self):
|
|
|
|
if not os.path.exists(self.root_img):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
return self.get_disk_usage(self.root_img)
|
|
|
|
|
|
|
|
def get_root_img_sz(self):
|
|
|
|
if not os.path.exists(self.root_img):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
return os.path.getsize(self.root_img)
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def get_power_state(self):
|
|
|
|
if dry_run:
|
|
|
|
return "NA"
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
dominfo = self.get_xc_dominfo()
|
|
|
|
if dominfo:
|
|
|
|
if dominfo['paused']:
|
|
|
|
return "Paused"
|
|
|
|
elif dominfo['crashed']:
|
|
|
|
return "Crashed"
|
2011-09-13 18:39:09 +02:00
|
|
|
elif dominfo['shutdown']:
|
|
|
|
return "Halting"
|
2011-06-01 23:44:06 +02:00
|
|
|
elif dominfo['dying']:
|
|
|
|
return "Dying"
|
|
|
|
else:
|
|
|
|
return "Running"
|
|
|
|
else:
|
|
|
|
return 'Halted'
|
2010-05-07 15:42:34 +02:00
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
return "NA"
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
def is_running(self):
|
|
|
|
if self.get_power_state() == "Running":
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2010-07-08 12:41:29 +02:00
|
|
|
def is_paused(self):
|
|
|
|
if self.get_power_state() == "Paused":
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2011-05-12 18:15:09 +02:00
|
|
|
def get_start_time(self):
|
|
|
|
if not self.is_running():
|
2011-10-01 02:54:18 +02:00
|
|
|
return None
|
2011-05-12 18:15:09 +02:00
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
dominfo = self.get_xl_dominfo()
|
|
|
|
|
|
|
|
uuid = self.get_uuid()
|
|
|
|
|
2011-06-25 22:31:22 +02:00
|
|
|
start_time = xs.read('', "/vm/%s/start_time" % str(uuid))
|
2011-10-01 02:54:18 +02:00
|
|
|
if start_time != '':
|
2012-02-07 23:17:53 +01:00
|
|
|
return datetime.fromtimestamp(float(start_time))
|
2011-10-01 02:54:18 +02:00
|
|
|
else:
|
|
|
|
return None
|
2011-05-12 18:15:09 +02:00
|
|
|
|
2011-04-02 02:11:41 +02:00
|
|
|
def is_outdated(self):
|
|
|
|
# Makes sense only on VM based on template
|
|
|
|
if self.template_vm is None:
|
|
|
|
return False
|
|
|
|
|
|
|
|
if not self.is_running():
|
|
|
|
return False
|
|
|
|
|
|
|
|
rootimg_inode = os.stat(self.template_vm.root_img)
|
|
|
|
rootcow_inode = os.stat(self.template_vm.rootcow_img)
|
2011-06-01 23:44:06 +02:00
|
|
|
|
2011-04-02 02:11:41 +02:00
|
|
|
current_dmdev = "/dev/mapper/snapshot-{0:x}:{1}-{2:x}:{3}".format(
|
|
|
|
rootimg_inode[2], rootimg_inode[1],
|
|
|
|
rootcow_inode[2], rootcow_inode[1])
|
|
|
|
|
|
|
|
# Don't know why, but 51712 is xvda
|
|
|
|
# backend node name not available through xenapi :(
|
2011-06-05 23:35:53 +02:00
|
|
|
used_dmdev = xs.read('', "/local/domain/0/backend/vbd/{0}/51712/node".format(self.get_xid()))
|
2011-04-02 02:11:41 +02:00
|
|
|
|
|
|
|
return used_dmdev != current_dmdev
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def get_disk_usage(self, file_or_dir):
|
|
|
|
if not os.path.exists(file_or_dir):
|
|
|
|
return 0
|
|
|
|
p = subprocess.Popen (["du", "-s", "--block-size=1", file_or_dir],
|
|
|
|
stdout=subprocess.PIPE)
|
|
|
|
result = p.communicate()
|
|
|
|
m = re.match(r"^(\d+)\s.*", result[0])
|
|
|
|
sz = int(m.group(1)) if m is not None else 0
|
|
|
|
return sz
|
|
|
|
|
|
|
|
def get_disk_utilization(self):
|
|
|
|
return self.get_disk_usage(self.dir_path)
|
|
|
|
|
|
|
|
def get_disk_utilization_private_img(self):
|
|
|
|
return self.get_disk_usage(self.private_img)
|
|
|
|
|
|
|
|
def get_private_img_sz(self):
|
|
|
|
if not os.path.exists(self.private_img):
|
|
|
|
return 0
|
2011-03-02 15:00:19 +01:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
return os.path.getsize(self.private_img)
|
|
|
|
|
2011-03-24 00:41:58 +01:00
|
|
|
def resize_private_img(self, size):
|
2012-02-10 20:47:21 +01:00
|
|
|
assert size >= self.get_private_img_sz(), "Cannot shrink private.img"
|
|
|
|
|
2011-03-24 00:41:58 +01:00
|
|
|
f_private = open (self.private_img, "a+b")
|
|
|
|
f_private.truncate (size)
|
|
|
|
f_private.close ()
|
|
|
|
|
2012-02-10 20:47:21 +01:00
|
|
|
retcode = 0
|
|
|
|
if self.is_running():
|
|
|
|
# find loop device
|
2012-02-29 23:11:29 +01:00
|
|
|
p = subprocess.Popen (["sudo", "losetup", "--associated", self.private_img],
|
2012-02-10 20:47:21 +01:00
|
|
|
stdout=subprocess.PIPE)
|
|
|
|
result = p.communicate()
|
|
|
|
m = re.match(r"^(/dev/loop\d+):\s", result[0])
|
|
|
|
if m is None:
|
|
|
|
raise QubesException("ERROR: Cannot find loop device!")
|
|
|
|
|
|
|
|
loop_dev = m.group(1)
|
|
|
|
|
|
|
|
# resize loop device
|
|
|
|
subprocess.check_call(["sudo", "losetup", "--set-capacity", loop_dev])
|
|
|
|
|
2012-02-29 23:11:29 +01:00
|
|
|
retcode = self.run("root:while [ \"`blockdev --getsize64 /dev/xvdb`\" -lt {0} ]; do ".format(size) +
|
|
|
|
"head /dev/xvdb > /dev/null; sleep 0.2; done; resize2fs /dev/xvdb", wait=True)
|
2012-02-10 20:47:21 +01:00
|
|
|
else:
|
2012-02-29 23:11:29 +01:00
|
|
|
retcode = subprocess.check_call(["sudo", "resize2fs", "-f", self.private_img])
|
2012-02-10 20:47:21 +01:00
|
|
|
if retcode != 0:
|
|
|
|
raise QubesException("resize2fs failed")
|
|
|
|
|
|
|
|
|
2012-01-18 16:59:58 +01:00
|
|
|
# FIXME: should be outside of QubesVM?
|
|
|
|
def get_timezone(self):
|
|
|
|
clock_config = open('/etc/sysconfig/clock', "r")
|
|
|
|
clock_config_lines = clock_config.readlines()
|
|
|
|
clock_config.close()
|
|
|
|
zone_re = re.compile(r'^ZONE="(.*)"')
|
|
|
|
for line in clock_config_lines:
|
|
|
|
line_match = zone_re.match(line)
|
|
|
|
if line_match:
|
|
|
|
return line_match.group(1)
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
2011-04-23 02:32:54 +02:00
|
|
|
def cleanup_vifs(self):
|
|
|
|
"""
|
|
|
|
Xend does not remove vif when backend domain is down, so we must do it
|
|
|
|
manually
|
|
|
|
"""
|
|
|
|
|
|
|
|
if not self.is_running():
|
|
|
|
return
|
|
|
|
|
2011-10-03 22:54:45 +02:00
|
|
|
dev_basepath = '/local/domain/%d/device/vif' % self.xid
|
2011-10-10 17:11:00 +02:00
|
|
|
for dev in xs.ls('', dev_basepath):
|
2011-10-03 22:54:45 +02:00
|
|
|
# check if backend domain is alive
|
|
|
|
backend_xid = int(xs.read('', '%s/%s/backend-id' % (dev_basepath, dev)))
|
|
|
|
if xl_ctx.domid_to_name(backend_xid) is not None:
|
|
|
|
# check if device is still active
|
|
|
|
if xs.read('', '%s/%s/state' % (dev_basepath, dev)) == '4':
|
|
|
|
continue
|
|
|
|
# remove dead device
|
|
|
|
xs.rm('', '%s/%s' % (dev_basepath, dev))
|
2012-02-10 20:43:29 +01:00
|
|
|
|
2011-09-06 01:14:49 +02:00
|
|
|
def create_xenstore_entries(self, xid = None):
|
2010-04-05 20:58:57 +02:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2011-09-06 01:14:49 +02:00
|
|
|
if xid is None:
|
|
|
|
xid = self.xid
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
domain_path = xs.get_domain_path(xid)
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
# Set Xen Store entires with VM networking info:
|
|
|
|
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.write('', "{0}/qubes_vm_type".format(domain_path),
|
2011-06-01 23:44:06 +02:00
|
|
|
self.type)
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.write('', "{0}/qubes_vm_updateable".format(domain_path),
|
2011-06-01 23:44:06 +02:00
|
|
|
str(self.updateable))
|
2011-04-20 01:01:38 +02:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
if self.is_netvm():
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.write('',
|
2011-06-01 23:44:06 +02:00
|
|
|
"{0}/qubes_netvm_gateway".format(domain_path),
|
|
|
|
self.gateway)
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.write('',
|
2011-06-01 23:44:06 +02:00
|
|
|
"{0}/qubes_netvm_secondary_dns".format(domain_path),
|
|
|
|
self.secondary_dns)
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.write('',
|
2011-06-01 23:44:06 +02:00
|
|
|
"{0}/qubes_netvm_netmask".format(domain_path),
|
|
|
|
self.netmask)
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.write('',
|
2011-06-01 23:44:06 +02:00
|
|
|
"{0}/qubes_netvm_network".format(domain_path),
|
|
|
|
self.network)
|
2011-03-02 15:01:30 +01:00
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
if self.netvm is not None:
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.write('', "{0}/qubes_ip".format(domain_path), self.ip)
|
|
|
|
xs.write('', "{0}/qubes_netmask".format(domain_path),
|
2012-03-04 21:59:02 +01:00
|
|
|
self.netvm.netmask)
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.write('', "{0}/qubes_gateway".format(domain_path),
|
2012-03-04 21:59:02 +01:00
|
|
|
self.netvm.gateway)
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.write('',
|
2011-06-01 23:44:06 +02:00
|
|
|
"{0}/qubes_secondary_dns".format(domain_path),
|
2012-03-04 21:59:02 +01:00
|
|
|
self.netvm.secondary_dns)
|
2011-06-01 23:44:06 +02:00
|
|
|
|
2012-01-18 16:59:58 +01:00
|
|
|
tzname = self.get_timezone()
|
|
|
|
if tzname:
|
|
|
|
xs.write('',
|
|
|
|
"{0}/qubes-timezone".format(domain_path),
|
|
|
|
tzname)
|
|
|
|
|
2011-10-01 02:49:25 +02:00
|
|
|
for srv in self.services.keys():
|
|
|
|
# convert True/False to "1"/"0"
|
|
|
|
xs.write('', "{0}/qubes-service/{1}".format(domain_path, srv),
|
|
|
|
str(int(self.services[srv])))
|
|
|
|
|
2011-09-29 13:49:52 +02:00
|
|
|
xs.write('',
|
|
|
|
"{0}/qubes-block-devices".format(domain_path),
|
|
|
|
'')
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
# Fix permissions
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.set_permissions('', '{0}/device'.format(domain_path),
|
2011-06-01 23:44:06 +02:00
|
|
|
[{ 'dom': xid }])
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.set_permissions('', '{0}/memory'.format(domain_path),
|
2011-06-01 23:44:06 +02:00
|
|
|
[{ 'dom': xid }])
|
2011-09-29 13:49:52 +02:00
|
|
|
xs.set_permissions('', '{0}/qubes-block-devices'.format(domain_path),
|
|
|
|
[{ 'dom': xid }])
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-06-06 01:43:52 +02:00
|
|
|
def get_rootdev(self, source_template=None):
|
|
|
|
if self.template_vm:
|
|
|
|
return "'script:snapshot:{dir}/root.img:{dir}/root-cow.img,xvda,r',".format(dir=self.template_vm.dir_path)
|
|
|
|
else:
|
|
|
|
return "'script:file:{dir}/root.img,xvda,w',".format(dir=self.dir_path)
|
|
|
|
|
|
|
|
def get_config_params(self, source_template=None):
|
|
|
|
args = {}
|
|
|
|
args['name'] = self.name
|
2012-02-24 04:22:14 +01:00
|
|
|
if hasattr(self, 'kernels_dir'):
|
|
|
|
args['kerneldir'] = self.kernels_dir
|
2011-06-06 01:43:52 +02:00
|
|
|
args['vmdir'] = self.dir_path
|
2011-07-05 22:01:28 +02:00
|
|
|
args['pcidev'] = str(self.pcidevs).strip('[]')
|
2011-06-06 01:43:52 +02:00
|
|
|
args['mem'] = str(self.memory)
|
|
|
|
args['maxmem'] = str(self.maxmem)
|
|
|
|
args['vcpus'] = str(self.vcpus)
|
2012-03-04 21:59:02 +01:00
|
|
|
if self.netvm is not None:
|
2012-03-02 16:07:31 +01:00
|
|
|
args['netdev'] = "'mac={mac},script=/etc/xen/scripts/vif-route-qubes,ip={ip}".format(ip=self.ip, mac=self.mac)
|
2012-03-04 21:59:02 +01:00
|
|
|
if self.netvm.qid != 0:
|
|
|
|
args['netdev'] += ",backend={0}".format(self.netvm.name)
|
2011-06-08 03:28:08 +02:00
|
|
|
args['netdev'] += "'"
|
|
|
|
else:
|
|
|
|
args['netdev'] = ''
|
2011-06-06 01:43:52 +02:00
|
|
|
args['rootdev'] = self.get_rootdev(source_template=source_template)
|
|
|
|
args['privatedev'] = "'script:file:{dir}/private.img,xvdb,w',".format(dir=self.dir_path)
|
|
|
|
args['volatiledev'] = "'script:file:{dir}/volatile.img,xvdc,w',".format(dir=self.dir_path)
|
2012-02-24 04:22:14 +01:00
|
|
|
if hasattr(self, 'kernel'):
|
|
|
|
modulesmode='r'
|
|
|
|
if self.is_updateable() and self.kernel is None:
|
|
|
|
modulesmode='w'
|
|
|
|
args['otherdevs'] = "'script:file:{dir}/modules.img,xvdd,{mode}',".format(dir=self.kernels_dir, mode=modulesmode)
|
|
|
|
if hasattr(self, 'kernelopts'):
|
|
|
|
args['kernelopts'] = self.kernelopts
|
2011-06-06 01:43:52 +02:00
|
|
|
|
|
|
|
return args
|
|
|
|
|
2011-06-08 03:30:42 +02:00
|
|
|
def create_config_file(self, file_path = None, source_template = None, prepare_dvm = False):
|
2011-06-06 01:43:52 +02:00
|
|
|
if file_path is None:
|
|
|
|
file_path = self.conf_file
|
2011-04-04 18:41:02 +02:00
|
|
|
if source_template is None:
|
|
|
|
source_template = self.template_vm
|
2011-04-01 01:14:18 +02:00
|
|
|
|
2012-02-24 04:17:45 +01:00
|
|
|
f_conf_template = open(self.config_file_template, 'r')
|
2011-06-06 01:43:52 +02:00
|
|
|
conf_template = f_conf_template.read()
|
|
|
|
f_conf_template.close()
|
|
|
|
|
|
|
|
template_params = self.get_config_params(source_template)
|
2011-06-08 03:30:42 +02:00
|
|
|
if prepare_dvm:
|
|
|
|
template_params['name'] = '%NAME%'
|
|
|
|
template_params['privatedev'] = ''
|
|
|
|
template_params['netdev'] = re.sub(r"ip=[0-9.]*", "ip=%IP%", template_params['netdev'])
|
2011-06-08 03:29:52 +02:00
|
|
|
conf_appvm = open(file_path, "w")
|
2011-06-06 01:43:52 +02:00
|
|
|
|
|
|
|
conf_appvm.write(conf_template.format(**template_params))
|
2011-04-01 01:14:18 +02:00
|
|
|
conf_appvm.close()
|
|
|
|
|
2011-04-04 18:41:02 +02:00
|
|
|
def create_on_disk(self, verbose, source_template = None):
|
|
|
|
if source_template is None:
|
|
|
|
source_template = self.template_vm
|
|
|
|
assert source_template is not None
|
2011-04-01 01:14:18 +02:00
|
|
|
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path)
|
2011-04-01 01:14:18 +02:00
|
|
|
os.mkdir (self.dir_path)
|
|
|
|
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Creating the VM config file: {0}".format(self.conf_file)
|
2011-04-01 01:14:18 +02:00
|
|
|
|
2011-04-04 18:41:02 +02:00
|
|
|
self.create_config_file(source_template = source_template)
|
2011-04-01 01:14:18 +02:00
|
|
|
|
2011-04-04 18:41:02 +02:00
|
|
|
template_priv = source_template.private_img
|
2011-04-01 01:14:18 +02:00
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Copying the template's private image: {0}".\
|
2011-04-01 01:14:18 +02:00
|
|
|
format(template_priv)
|
|
|
|
|
|
|
|
# We prefer to use Linux's cp, because it nicely handles sparse files
|
|
|
|
retcode = subprocess.call (["cp", template_priv, self.private_img])
|
|
|
|
if retcode != 0:
|
|
|
|
raise IOError ("Error while copying {0} to {1}".\
|
|
|
|
format(template_priv, self.private_img))
|
|
|
|
|
2011-07-05 21:20:43 +02:00
|
|
|
if os.path.exists(source_template.dir_path + '/vm-' + qubes_whitelisted_appmenus):
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Creating default whitelisted apps list: {0}".\
|
2011-07-05 21:20:43 +02:00
|
|
|
format(self.dir_path + '/' + qubes_whitelisted_appmenus)
|
|
|
|
shutil.copy(source_template.dir_path + '/vm-' + qubes_whitelisted_appmenus,
|
|
|
|
self.dir_path + '/' + qubes_whitelisted_appmenus)
|
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
if self.is_updateable():
|
2011-04-04 18:41:02 +02:00
|
|
|
template_root = source_template.root_img
|
2011-04-01 01:14:18 +02:00
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Copying the template's root image: {0}".\
|
2011-04-01 01:14:18 +02:00
|
|
|
format(template_root)
|
|
|
|
|
|
|
|
# We prefer to use Linux's cp, because it nicely handles sparse files
|
|
|
|
retcode = subprocess.call (["cp", template_root, self.root_img])
|
|
|
|
if retcode != 0:
|
|
|
|
raise IOError ("Error while copying {0} to {1}".\
|
|
|
|
format(template_root, self.root_img))
|
|
|
|
|
2011-09-01 15:01:37 +02:00
|
|
|
kernels_dir = source_template.kernels_dir
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Copying the kernel (set kernel \"none\" to use it): {0}".\
|
2011-09-01 15:01:37 +02:00
|
|
|
format(kernels_dir)
|
|
|
|
|
2011-09-03 16:04:25 +02:00
|
|
|
os.mkdir (self.dir_path + '/kernels')
|
|
|
|
for f in ("vmlinuz", "initramfs", "modules.img"):
|
|
|
|
shutil.copy(kernels_dir + '/' + f, self.dir_path + '/kernels/' + f)
|
2011-09-01 15:01:37 +02:00
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
# Create volatile.img
|
2011-07-02 13:30:10 +02:00
|
|
|
self.reset_volatile_storage(source_template = source_template, verbose=verbose)
|
2011-04-01 01:14:18 +02:00
|
|
|
|
2011-04-19 13:38:24 +02:00
|
|
|
def create_appmenus(self, verbose, source_template = None):
|
|
|
|
if source_template is None:
|
|
|
|
source_template = self.template_vm
|
|
|
|
|
2011-07-20 16:06:22 +02:00
|
|
|
vmtype = None
|
|
|
|
if self.is_netvm():
|
|
|
|
vmtype = 'servicevms'
|
|
|
|
else:
|
|
|
|
vmtype = 'appvms'
|
|
|
|
|
2011-04-19 13:38:24 +02:00
|
|
|
try:
|
|
|
|
if source_template is not None:
|
|
|
|
subprocess.check_call ([qubes_appmenu_create_cmd, source_template.appmenus_templates_dir, self.name])
|
2011-09-06 01:15:35 +02:00
|
|
|
elif self.appmenus_templates_dir is not None:
|
|
|
|
subprocess.check_call ([qubes_appmenu_create_cmd, self.appmenus_templates_dir, self.name])
|
2011-04-19 13:38:24 +02:00
|
|
|
else:
|
|
|
|
# Only add apps to menu
|
|
|
|
subprocess.check_call ([qubes_appmenu_create_cmd, "none", self.name, vmtype])
|
|
|
|
except subprocess.CalledProcessError:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "Ooops, there was a problem creating appmenus for {0} VM!".format (self.name)
|
2011-04-19 13:38:24 +02:00
|
|
|
|
2012-03-02 16:06:26 +01:00
|
|
|
def get_clone_attrs(self):
|
2012-03-04 21:59:02 +01:00
|
|
|
return ['kernel', 'uses_default_kernel', 'netvm', 'uses_default_netvm', \
|
2012-03-02 16:29:56 +01:00
|
|
|
'memory', 'maxmem', 'kernelopts', 'uses_default_kernelopts', 'services', 'vcpus', \
|
|
|
|
'_mac']
|
2012-03-02 16:06:26 +01:00
|
|
|
|
|
|
|
def clone_attrs(self, src_vm):
|
|
|
|
for prop in self.get_clone_attrs():
|
|
|
|
setattr(self, prop, getattr(src_vm, prop))
|
|
|
|
|
|
|
|
def clone_disk_files(self, src_vm, verbose):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
if src_vm.is_running():
|
|
|
|
raise QubesException("Attempt to clone a running VM!")
|
|
|
|
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path)
|
|
|
|
os.mkdir (self.dir_path)
|
|
|
|
|
|
|
|
if src_vm.private_img is not None and self.private_img is not None:
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Copying the private image:\n{0} ==>\n{1}".\
|
|
|
|
format(src_vm.private_img, self.private_img)
|
|
|
|
# We prefer to use Linux's cp, because it nicely handles sparse files
|
|
|
|
retcode = subprocess.call (["cp", src_vm.private_img, self.private_img])
|
|
|
|
if retcode != 0:
|
|
|
|
raise IOError ("Error while copying {0} to {1}".\
|
|
|
|
format(src_vm.private_img, self.private_img))
|
|
|
|
|
|
|
|
if src_vm.updateable and src_vm.root_img is not None and self.root_img is not None:
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Copying the root image:\n{0} ==>\n{1}".\
|
|
|
|
format(src_vm.root_img, self.root_img)
|
|
|
|
# We prefer to use Linux's cp, because it nicely handles sparse files
|
|
|
|
retcode = subprocess.call (["cp", src_vm.root_img, self.root_img])
|
|
|
|
if retcode != 0:
|
|
|
|
raise IOError ("Error while copying {0} to {1}".\
|
|
|
|
format(src_vm.root_img, self.root_img))
|
|
|
|
|
|
|
|
if src_vm.updateable and src_vm.appmenus_templates_dir is not None and self.appmenus_templates_dir is not None:
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}".\
|
|
|
|
format(src_vm.appmenus_templates_dir, self.appmenus_templates_dir)
|
|
|
|
shutil.copytree (src_vm.appmenus_templates_dir, self.appmenus_templates_dir)
|
|
|
|
|
|
|
|
if os.path.exists(src_vm.dir_path + '/' + qubes_whitelisted_appmenus):
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Copying whitelisted apps list: {0}".\
|
|
|
|
format(self.dir_path + '/' + qubes_whitelisted_appmenus)
|
|
|
|
shutil.copy(src_vm.dir_path + '/' + qubes_whitelisted_appmenus,
|
|
|
|
self.dir_path + '/' + qubes_whitelisted_appmenus)
|
|
|
|
|
|
|
|
if src_vm.icon_path is not None and self.icon_path is not None:
|
|
|
|
if os.path.exists (src_vm.dir_path):
|
|
|
|
if os.path.islink(src_vm.dir_path):
|
|
|
|
icon_path = os.readlink(src_vm.dir_path)
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, icon_path)
|
|
|
|
os.symlink (icon_path, self.icon_path)
|
|
|
|
else:
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Copying icon: {0} -> {1}".format(src_vm.icon_path, self.icon_path)
|
|
|
|
shutil.copy(src_vm.icon_path, self.icon_path)
|
|
|
|
|
|
|
|
# Create appmenus
|
|
|
|
self.create_appmenus(verbose)
|
|
|
|
|
2011-11-01 23:42:13 +01:00
|
|
|
def remove_appmenus(self):
|
|
|
|
vmtype = None
|
|
|
|
if self.is_netvm():
|
|
|
|
vmtype = 'servicevms'
|
|
|
|
else:
|
|
|
|
vmtype = 'appvms'
|
|
|
|
subprocess.check_call ([qubes_appmenu_remove_cmd, self.name, vmtype])
|
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
def verify_files(self):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
if not os.path.exists (self.dir_path):
|
|
|
|
raise QubesException (
|
|
|
|
"VM directory doesn't exist: {0}".\
|
|
|
|
format(self.dir_path))
|
|
|
|
|
|
|
|
if self.is_updateable() and not os.path.exists (self.root_img):
|
|
|
|
raise QubesException (
|
|
|
|
"VM root image file doesn't exist: {0}".\
|
|
|
|
format(self.root_img))
|
|
|
|
|
|
|
|
if not os.path.exists (self.private_img):
|
|
|
|
raise QubesException (
|
|
|
|
"VM private image file doesn't exist: {0}".\
|
|
|
|
format(self.private_img))
|
2011-06-30 01:07:47 +02:00
|
|
|
|
|
|
|
if not os.path.exists (self.kernels_dir + '/vmlinuz'):
|
|
|
|
raise QubesException (
|
|
|
|
"VM kernel does not exists: {0}".\
|
|
|
|
format(self.kernels_dir + '/vmlinuz'))
|
|
|
|
|
|
|
|
if not os.path.exists (self.kernels_dir + '/initramfs'):
|
|
|
|
raise QubesException (
|
|
|
|
"VM initramfs does not exists: {0}".\
|
|
|
|
format(self.kernels_dir + '/initramfs'))
|
|
|
|
|
|
|
|
if not os.path.exists (self.kernels_dir + '/modules.img'):
|
|
|
|
raise QubesException (
|
|
|
|
"VM kernel modules image does not exists: {0}".\
|
|
|
|
format(self.kernels_dir + '/modules.img'))
|
2011-04-01 01:14:18 +02:00
|
|
|
return True
|
|
|
|
|
2011-07-02 13:30:10 +02:00
|
|
|
def reset_volatile_storage(self, source_template = None, verbose = False):
|
2011-04-01 01:14:18 +02:00
|
|
|
assert not self.is_running(), "Attempt to clean volatile image of running VM!"
|
|
|
|
|
2011-04-04 18:41:02 +02:00
|
|
|
if source_template is None:
|
|
|
|
source_template = self.template_vm
|
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
# Only makes sense on template based VM
|
2011-04-04 18:41:02 +02:00
|
|
|
if source_template is None:
|
2011-04-01 01:14:18 +02:00
|
|
|
return
|
|
|
|
|
2011-07-02 13:30:10 +02:00
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Cleaning volatile image: {0}...".format (self.volatile_img)
|
2011-04-01 01:14:18 +02:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
if os.path.exists (self.volatile_img):
|
|
|
|
os.remove (self.volatile_img)
|
|
|
|
|
2011-04-04 18:41:02 +02:00
|
|
|
retcode = subprocess.call (["tar", "xf", source_template.clean_volatile_img, "-C", self.dir_path])
|
2011-04-01 01:14:18 +02:00
|
|
|
if retcode != 0:
|
|
|
|
raise IOError ("Error while unpacking {0} to {1}".\
|
2011-04-04 18:41:02 +02:00
|
|
|
format(source_template.clean_volatile_img, self.volatile_img))
|
2011-04-01 01:14:18 +02:00
|
|
|
|
|
|
|
def remove_from_disk(self):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
shutil.rmtree (self.dir_path)
|
|
|
|
|
|
|
|
def write_firewall_conf(self, conf):
|
|
|
|
root = xml.etree.ElementTree.Element(
|
|
|
|
"QubesFirwallRules",
|
|
|
|
policy = "allow" if conf["allow"] else "deny",
|
|
|
|
dns = "allow" if conf["allowDns"] else "deny",
|
|
|
|
icmp = "allow" if conf["allowIcmp"] else "deny"
|
|
|
|
)
|
|
|
|
|
|
|
|
for rule in conf["rules"]:
|
2012-02-27 02:02:42 +01:00
|
|
|
# For backward compatibility
|
|
|
|
if "proto" not in rule:
|
2012-02-27 15:46:23 +01:00
|
|
|
if rule["portBegin"] is not None and rule["portBegin"] > 0:
|
|
|
|
rule["proto"] = "tcp"
|
|
|
|
else:
|
|
|
|
rule["proto"] = "any"
|
2011-04-01 01:14:18 +02:00
|
|
|
element = xml.etree.ElementTree.Element(
|
|
|
|
"rule",
|
|
|
|
address=rule["address"],
|
2012-02-27 02:02:42 +01:00
|
|
|
proto=str(rule["proto"]),
|
2011-04-01 01:14:18 +02:00
|
|
|
)
|
|
|
|
if rule["netmask"] is not None and rule["netmask"] != 32:
|
|
|
|
element.set("netmask", str(rule["netmask"]))
|
2012-02-27 02:02:42 +01:00
|
|
|
if rule["portBegin"] is not None and rule["portBegin"] > 0:
|
|
|
|
element.set("port", str(rule["portBegin"]))
|
|
|
|
if rule["portEnd"] is not None and rule["portEnd"] > 0:
|
2011-04-01 01:14:18 +02:00
|
|
|
element.set("toport", str(rule["portEnd"]))
|
2012-02-27 02:02:42 +01:00
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
root.append(element)
|
|
|
|
|
|
|
|
tree = xml.etree.ElementTree.ElementTree(root)
|
|
|
|
|
|
|
|
try:
|
|
|
|
f = open(self.firewall_conf, 'a') # create the file if not exist
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
with open(self.firewall_conf, 'w') as f:
|
|
|
|
fcntl.lockf(f, fcntl.LOCK_EX)
|
|
|
|
tree.write(f, "UTF-8")
|
|
|
|
fcntl.lockf(f, fcntl.LOCK_UN)
|
|
|
|
f.close()
|
|
|
|
except EnvironmentError as err:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "{0}: save error: {1}".format(
|
2011-04-01 01:14:18 +02:00
|
|
|
os.path.basename(sys.argv[0]), err)
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
def has_firewall(self):
|
|
|
|
return os.path.exists (self.firewall_conf)
|
|
|
|
|
|
|
|
def get_firewall_conf(self):
|
|
|
|
conf = { "rules": list(), "allow": True, "allowDns": True, "allowIcmp": True }
|
|
|
|
|
|
|
|
try:
|
|
|
|
tree = xml.etree.ElementTree.parse(self.firewall_conf)
|
|
|
|
root = tree.getroot()
|
|
|
|
|
|
|
|
conf["allow"] = (root.get("policy") == "allow")
|
|
|
|
conf["allowDns"] = (root.get("dns") == "allow")
|
|
|
|
conf["allowIcmp"] = (root.get("icmp") == "allow")
|
|
|
|
|
|
|
|
for element in root:
|
|
|
|
rule = {}
|
2012-02-27 02:02:42 +01:00
|
|
|
attr_list = ("address", "netmask", "proto", "port", "toport")
|
2011-04-01 01:14:18 +02:00
|
|
|
|
|
|
|
for attribute in attr_list:
|
|
|
|
rule[attribute] = element.get(attribute)
|
|
|
|
|
|
|
|
if rule["netmask"] is not None:
|
|
|
|
rule["netmask"] = int(rule["netmask"])
|
|
|
|
else:
|
|
|
|
rule["netmask"] = 32
|
|
|
|
|
2012-02-27 02:02:42 +01:00
|
|
|
if rule["port"] is not None:
|
|
|
|
rule["portBegin"] = int(rule["port"])
|
|
|
|
else:
|
|
|
|
# backward compatibility
|
|
|
|
rule["portBegin"] = 0
|
2011-04-01 01:14:18 +02:00
|
|
|
|
2012-02-27 15:46:23 +01:00
|
|
|
# For backward compatibility
|
|
|
|
if rule["proto"] is None:
|
|
|
|
if rule["portBegin"] > 0:
|
|
|
|
rule["proto"] = "tcp"
|
|
|
|
else:
|
|
|
|
rule["proto"] = "any"
|
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
if rule["toport"] is not None:
|
|
|
|
rule["portEnd"] = int(rule["toport"])
|
|
|
|
else:
|
|
|
|
rule["portEnd"] = None
|
|
|
|
|
|
|
|
del(rule["port"])
|
|
|
|
del(rule["toport"])
|
|
|
|
|
|
|
|
conf["rules"].append(rule)
|
|
|
|
|
|
|
|
except EnvironmentError as err:
|
|
|
|
return conf
|
|
|
|
except (xml.parsers.expat.ExpatError,
|
|
|
|
ValueError, LookupError) as err:
|
|
|
|
print("{0}: load error: {1}".format(
|
|
|
|
os.path.basename(sys.argv[0]), err))
|
|
|
|
return None
|
|
|
|
|
|
|
|
return conf
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2012-02-10 20:44:28 +01:00
|
|
|
def run(self, command, verbose = True, autostart = False, notify_function = None, passio = False, passio_popen = False, localcmd = None, wait = False):
|
|
|
|
"""command should be in form 'user:cmdline'"""
|
|
|
|
|
|
|
|
if not self.is_running():
|
|
|
|
if not autostart:
|
|
|
|
raise QubesException("VM not running")
|
|
|
|
|
|
|
|
try:
|
|
|
|
if notify_function is not None:
|
|
|
|
notify_function ("info", "Starting the '{0}' VM...".format(self.name))
|
|
|
|
elif verbose:
|
|
|
|
print >> sys.stderr, "Starting the VM '{0}'...".format(self.name)
|
|
|
|
xid = self.start(verbose=verbose)
|
|
|
|
|
|
|
|
except (IOError, OSError, QubesException) as err:
|
|
|
|
raise QubesException("Error while starting the '{0}' VM: {1}".format(self.name, err))
|
|
|
|
except (MemoryError) as err:
|
|
|
|
raise QubesException("Not enough memory to start '{0}' VM! Close one or more running VMs and try again.".format(self.name))
|
|
|
|
|
|
|
|
xid = self.get_xid()
|
|
|
|
if os.getenv("DISPLAY") is not None and not os.path.isfile("/var/run/qubes/guid_running.{0}".format(xid)):
|
|
|
|
self.start_guid(verbose = verbose, notify_function = notify_function)
|
|
|
|
|
|
|
|
args = [qrexec_client_path, "-d", str(xid), command]
|
|
|
|
if localcmd is not None:
|
|
|
|
args += [ "-l", localcmd]
|
|
|
|
if passio:
|
|
|
|
os.execv(qrexec_client_path, args)
|
|
|
|
exit(1)
|
|
|
|
if passio_popen:
|
|
|
|
p = subprocess.Popen (args, stdout=subprocess.PIPE)
|
|
|
|
return p
|
|
|
|
if not wait:
|
|
|
|
args += ["-e"]
|
|
|
|
return subprocess.call(args)
|
|
|
|
|
2011-09-03 16:07:10 +02:00
|
|
|
def attach_network(self, verbose = False, wait = True, netvm = None):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
if not self.is_running():
|
|
|
|
raise QubesException ("VM not running!")
|
|
|
|
|
|
|
|
if netvm is None:
|
2012-03-04 21:59:02 +01:00
|
|
|
netvm = self.netvm
|
2011-09-03 16:07:10 +02:00
|
|
|
|
|
|
|
if netvm is None:
|
|
|
|
raise QubesException ("NetVM not set!")
|
|
|
|
|
|
|
|
if netvm.qid != 0:
|
|
|
|
if not netvm.is_running():
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Starting NetVM {0}...".format(netvm.name)
|
2011-09-03 16:07:10 +02:00
|
|
|
netvm.start()
|
|
|
|
|
|
|
|
xs_path = '/local/domain/%d/device/vif/0/state' % (self.xid)
|
|
|
|
if xs.read('', xs_path) is not None:
|
|
|
|
# TODO: check its state and backend state (this can be stale vif after NetVM restart)
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "NOTICE: Network already attached"
|
2011-09-03 16:07:10 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
xm_cmdline = ["/usr/sbin/xl", "network-attach", str(self.xid), "script=/etc/xen/scripts/vif-route-qubes", "ip="+self.ip, "backend="+netvm.name ]
|
|
|
|
retcode = subprocess.call (xm_cmdline)
|
|
|
|
if retcode != 0:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, ("WARNING: Cannot attach to network to '{0}'!".format(self.name))
|
2011-09-03 16:07:10 +02:00
|
|
|
if wait:
|
|
|
|
tries = 0
|
|
|
|
while xs.read('', xs_path) != '4':
|
|
|
|
tries += 1
|
|
|
|
if tries > 50:
|
|
|
|
raise QubesException ("Network attach timed out!")
|
|
|
|
time.sleep(0.2)
|
|
|
|
|
2011-12-05 13:36:13 +01:00
|
|
|
def start_guid(self, verbose = True, notify_function = None):
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Starting Qubes GUId..."
|
|
|
|
xid = self.get_xid()
|
|
|
|
|
|
|
|
retcode = subprocess.call ([qubes_guid_path, "-d", str(xid), "-c", self.label.color, "-i", self.label.icon, "-l", str(self.label.index)])
|
|
|
|
if (retcode != 0) :
|
|
|
|
raise QubesException("Cannot start qubes_guid!")
|
|
|
|
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Waiting for qubes-session..."
|
|
|
|
|
|
|
|
subprocess.call([qrexec_client_path, "-d", str(xid), "user:echo $$ >> /tmp/qubes-session-waiter; [ ! -f /tmp/qubes-session-env ] && exec sleep 365d"])
|
|
|
|
|
|
|
|
retcode = subprocess.call([qubes_clipd_path])
|
|
|
|
if retcode != 0:
|
|
|
|
print >> sys.stderr, "ERROR: Cannot start qclipd!"
|
|
|
|
if notify_function is not None:
|
|
|
|
notify_function("error", "ERROR: Cannot start the Qubes Clipboard Notifier!")
|
|
|
|
|
2012-02-24 04:18:45 +01:00
|
|
|
def start_qrexec_daemon(self, verbose = False):
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Starting the qrexec daemon..."
|
2012-02-26 00:58:05 +01:00
|
|
|
xid = self.get_xid()
|
2012-02-24 04:18:45 +01:00
|
|
|
retcode = subprocess.call ([qrexec_daemon_path, str(xid)])
|
|
|
|
if (retcode != 0) :
|
|
|
|
self.force_shutdown()
|
|
|
|
raise OSError ("ERROR: Cannot execute qrexec_daemon!")
|
|
|
|
|
2010-09-07 16:15:24 +02:00
|
|
|
def start(self, debug_console = False, verbose = False, preparing_dvm = False):
|
2010-04-05 20:58:57 +02:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2011-09-13 18:39:09 +02:00
|
|
|
# Intentionally not used is_running(): eliminate also "Paused", "Crashed", "Halting"
|
|
|
|
if self.get_power_state() != "Halted":
|
2010-04-05 20:58:57 +02:00
|
|
|
raise QubesException ("VM is already running!")
|
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
if self.netvm is not None:
|
|
|
|
if self.netvm.qid != 0:
|
|
|
|
if not self.netvm.is_running():
|
2011-06-08 03:28:08 +02:00
|
|
|
if verbose:
|
2012-03-04 21:59:02 +01:00
|
|
|
print >> sys.stderr, "--> Starting NetVM {0}...".format(self.netvm.name)
|
|
|
|
self.netvm.start()
|
2011-06-08 03:28:08 +02:00
|
|
|
|
2011-07-02 13:30:10 +02:00
|
|
|
self.reset_volatile_storage(verbose=verbose)
|
2010-04-05 20:58:57 +02:00
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Loading the VM (type = {0})...".format(self.type)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-06-06 01:43:52 +02:00
|
|
|
# refresh config file
|
|
|
|
self.create_config_file()
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
mem_required = int(self.memory) * 1024 * 1024
|
2010-08-30 11:40:19 +02:00
|
|
|
qmemman_client = QMemmanClient()
|
|
|
|
if not qmemman_client.request_memory(mem_required):
|
|
|
|
qmemman_client.close()
|
|
|
|
raise MemoryError ("ERROR: insufficient memory to start this VM")
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-07-05 22:10:09 +02:00
|
|
|
# Bind pci devices to pciback driver
|
|
|
|
for pci in self.pcidevs:
|
2011-07-09 20:43:57 +02:00
|
|
|
subprocess.check_call(['sudo', qubes_pciback_cmd, pci])
|
2011-07-05 22:10:09 +02:00
|
|
|
|
2011-07-17 01:53:44 +02:00
|
|
|
xl_cmdline = ['sudo', '/usr/sbin/xl', 'create', self.conf_file, '-q', '-p']
|
2011-06-01 23:44:06 +02:00
|
|
|
|
2010-05-08 16:28:13 +02:00
|
|
|
try:
|
2011-06-01 23:44:06 +02:00
|
|
|
subprocess.check_call(xl_cmdline)
|
2011-06-05 22:58:20 +02:00
|
|
|
except:
|
2011-06-01 23:44:06 +02:00
|
|
|
raise QubesException("Failed to load VM config")
|
2011-03-02 15:00:19 +01:00
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
xid = self.get_xid()
|
|
|
|
self.xid = xid
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2012-01-14 01:34:35 +01:00
|
|
|
if preparing_dvm:
|
|
|
|
self.services['qubes-dvm'] = True
|
2010-04-05 20:58:57 +02:00
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Setting Xen Store info for the VM..."
|
2010-04-05 20:58:57 +02:00
|
|
|
self.create_xenstore_entries(xid)
|
|
|
|
|
2011-03-09 17:38:34 +01:00
|
|
|
qvm_collection = QubesVmCollection()
|
|
|
|
qvm_collection.lock_db_for_reading()
|
|
|
|
qvm_collection.load()
|
|
|
|
qvm_collection.unlock_db()
|
|
|
|
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Updating firewall rules..."
|
2011-03-09 17:38:34 +01:00
|
|
|
for vm in qvm_collection.values():
|
2011-06-07 12:19:17 +02:00
|
|
|
if vm.is_proxyvm() and vm.is_running():
|
2011-03-09 17:38:34 +01:00
|
|
|
vm.write_iptables_xenstore_entry()
|
2011-03-02 15:04:11 +01:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Starting the VM..."
|
2011-06-01 23:44:06 +02:00
|
|
|
xc.domain_unpause(xid)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-10-10 11:07:30 +02:00
|
|
|
# close() is not really needed, because the descriptor is close-on-exec
|
|
|
|
# anyway, the reason to postpone close() is that possibly xl is not done
|
|
|
|
# constructing the domain after its main process exits
|
|
|
|
# so we close() when we know the domain is up
|
|
|
|
# the successful unpause is some indicator of it
|
|
|
|
qmemman_client.close()
|
|
|
|
|
2011-03-31 11:11:39 +02:00
|
|
|
if not preparing_dvm:
|
2012-02-24 04:18:45 +01:00
|
|
|
self.start_qrexec_daemon(verbose=verbose)
|
2011-03-31 11:11:39 +02:00
|
|
|
|
2011-12-27 15:37:23 +01:00
|
|
|
if not preparing_dvm and os.path.exists('/var/run/shm.id'):
|
2011-12-05 13:42:07 +01:00
|
|
|
self.start_guid(verbose=verbose)
|
2011-12-05 13:36:13 +01:00
|
|
|
|
2011-06-08 03:30:42 +02:00
|
|
|
if preparing_dvm:
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Preparing config template for DispVM"
|
2011-06-08 03:30:42 +02:00
|
|
|
self.create_config_file(file_path = self.dir_path + '/dvm.conf', prepare_dvm = True)
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
# perhaps we should move it before unpause and fork?
|
2011-06-01 23:44:06 +02:00
|
|
|
# FIXME: this uses obsolete xm api
|
2010-04-05 20:58:57 +02:00
|
|
|
if debug_console:
|
|
|
|
from xen.xm import console
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Starting debug console..."
|
2010-04-05 20:58:57 +02:00
|
|
|
console.execConsole (xid)
|
|
|
|
|
|
|
|
return xid
|
|
|
|
|
2011-10-17 22:15:10 +02:00
|
|
|
def shutdown(self, force=False):
|
2011-10-14 11:59:33 +02:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
subprocess.call (['/usr/sbin/xl', 'shutdown', self.name])
|
|
|
|
#xc.domain_destroy(self.get_xid())
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def force_shutdown(self):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2011-06-01 23:44:06 +02:00
|
|
|
subprocess.call (['/usr/sbin/xl', 'destroy', self.name])
|
|
|
|
#xc.domain_destroy(self.get_xid())
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
def remove_from_disk(self):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
shutil.rmtree (self.dir_path)
|
|
|
|
|
2011-03-17 00:42:01 +01:00
|
|
|
def get_xml_attrs(self):
|
|
|
|
attrs = {}
|
|
|
|
attrs["qid"] = str(self.qid)
|
|
|
|
attrs["name"] = self.name
|
|
|
|
attrs["dir_path"] = self.dir_path
|
2012-02-24 04:19:51 +01:00
|
|
|
# Simple paths
|
|
|
|
for prop in ['conf_file', 'root_img', 'volatile_img', 'private_img']:
|
2012-03-02 12:04:57 +01:00
|
|
|
if hasattr(self, prop) and self.__getattribute__(prop) is not None:
|
2012-02-24 04:19:51 +01:00
|
|
|
attrs[prop] = self.relative_path(self.__getattribute__(prop))
|
|
|
|
# Simple string attrs
|
|
|
|
for prop in ['memory', 'maxmem', 'pcidevs', 'vcpus', 'internal',\
|
|
|
|
'uses_default_kernel', 'kernel', 'uses_default_kernelopts',\
|
|
|
|
'kernelopts', 'services', 'updateable', 'installed_by_rpm',\
|
|
|
|
'uses_default_netvm' ]:
|
|
|
|
if hasattr(self, prop):
|
|
|
|
attrs[prop] = str(self.__getattribute__(prop))
|
2012-03-02 16:07:31 +01:00
|
|
|
if self._mac is not None:
|
|
|
|
attrs["mac"] = str(self._mac)
|
2012-03-04 21:59:02 +01:00
|
|
|
attrs["netvm_qid"] = str(self.netvm.qid) if self.netvm is not None else "none"
|
2011-04-01 01:14:18 +02:00
|
|
|
attrs["template_qid"] = str(self.template_vm.qid) if self.template_vm and not self.is_updateable() else "none"
|
2011-03-17 00:42:01 +01:00
|
|
|
attrs["label"] = self.label.name
|
|
|
|
return attrs
|
|
|
|
|
|
|
|
def create_xml_element(self):
|
|
|
|
# Compatibility hack (Qubes*VM in type vs Qubes*Vm in XML)...
|
|
|
|
rx_type = re.compile (r"VM")
|
|
|
|
|
|
|
|
attrs = self.get_xml_attrs()
|
|
|
|
element = xml.etree.ElementTree.Element(
|
|
|
|
"Qubes" + rx_type.sub("Vm", self.type),
|
|
|
|
**attrs)
|
|
|
|
return element
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
class QubesTemplateVm(QubesVm):
|
|
|
|
"""
|
2011-03-06 17:06:45 +01:00
|
|
|
A class that represents an TemplateVM. A child of QubesVm.
|
2010-04-05 20:58:57 +02:00
|
|
|
"""
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
|
|
|
|
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
|
|
|
|
kwargs["dir_path"] = qubes_templates_dir + "/" + kwargs["name"]
|
|
|
|
|
|
|
|
if "updateable" not in kwargs or kwargs["updateable"] is None :
|
|
|
|
kwargs["updateable"] = True
|
|
|
|
|
2011-03-17 01:40:15 +01:00
|
|
|
if "label" not in kwargs or kwargs["label"] == None:
|
|
|
|
kwargs["label"] = default_template_label
|
|
|
|
|
|
|
|
super(QubesTemplateVm, self).__init__(**kwargs)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
dir_path = kwargs["dir_path"]
|
2011-03-19 03:15:32 +01:00
|
|
|
|
|
|
|
# Clean image for root-cow and swap (AppVM side)
|
|
|
|
self.clean_volatile_img = self.dir_path + "/" + default_clean_volatile_img
|
2011-06-30 01:07:47 +02:00
|
|
|
|
2011-03-19 03:15:32 +01:00
|
|
|
# Image for template changes
|
|
|
|
self.rootcow_img = self.dir_path + "/" + default_rootcow_img
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
self.appmenus_templates_dir = self.dir_path + "/" + default_appmenus_templates_subdir
|
|
|
|
self.appvms = QubesVmCollection()
|
|
|
|
|
2011-02-09 21:35:36 +01:00
|
|
|
@property
|
|
|
|
def type(self):
|
2011-03-11 01:52:09 +01:00
|
|
|
return "TemplateVM"
|
2011-02-09 21:35:36 +01:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def set_updateable(self):
|
|
|
|
if self.is_updateable():
|
|
|
|
return
|
|
|
|
|
|
|
|
assert not self.is_running()
|
|
|
|
# Make sure that all the AppVMs are non-updateable...
|
|
|
|
for appvm in self.appvms.values():
|
|
|
|
if appvm.is_updateable():
|
|
|
|
raise QubesException("One of the AppVMs ('{0}')is also 'updateable'\
|
2011-03-11 01:52:09 +01:00
|
|
|
-- cannot make the TemplateVM {'{1}'} 'nonupdatable'".\
|
2010-04-05 20:58:57 +02:00
|
|
|
format (appvm.name, self.name))
|
|
|
|
self.updateable = True
|
|
|
|
|
2011-06-06 01:43:52 +02:00
|
|
|
def get_rootdev(self, source_template=None):
|
|
|
|
return "'script:origin:{dir}/root.img:{dir}/root-cow.img,xvda,w',".format(dir=self.dir_path)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2012-03-02 16:06:26 +01:00
|
|
|
def clone_disk_files(self, src_vm, verbose):
|
2010-04-05 20:58:57 +02:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2012-03-02 16:06:26 +01:00
|
|
|
super(QubesTemplateVM, self).clone_disk_files(src_vm=src_vm, verbose=verbose)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2012-03-02 16:06:26 +01:00
|
|
|
if os.path.exists(src_vm.dir_path + '/vm-' + qubes_whitelisted_appmenus):
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Copying default whitelisted apps list: {0}".\
|
|
|
|
format(self.dir_path + '/vm-' + qubes_whitelisted_appmenus)
|
|
|
|
shutil.copy(src_vm.dir_path + '/vm-' + qubes_whitelisted_appmenus,
|
|
|
|
self.dir_path + '/vm-' + qubes_whitelisted_appmenus)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-02-27 00:06:46 +01:00
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Copying the template's clean volatile image:\n{0} ==>\n{1}".\
|
2012-03-02 16:06:26 +01:00
|
|
|
format(src_vm.clean_volatile_img, self.clean_volatile_img)
|
2011-02-27 00:06:46 +01:00
|
|
|
# We prefer to use Linux's cp, because it nicely handles sparse files
|
2012-03-02 16:06:26 +01:00
|
|
|
retcode = subprocess.call (["cp", src_vm.clean_volatile_img, self.clean_volatile_img])
|
2010-04-05 20:58:57 +02:00
|
|
|
if retcode != 0:
|
|
|
|
raise IOError ("Error while copying {0} to {1}".\
|
2011-03-19 03:15:32 +01:00
|
|
|
format(src_template_vm.clean_volatile_img, self.clean_volatile_img))
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Copying the template's volatile image:\n{0} ==>\n{1}".\
|
2011-03-19 03:15:32 +01:00
|
|
|
format(self.clean_volatile_img, self.volatile_img)
|
|
|
|
# We prefer to use Linux's cp, because it nicely handles sparse files
|
|
|
|
retcode = subprocess.call (["cp", self.clean_volatile_img, self.volatile_img])
|
|
|
|
if retcode != 0:
|
|
|
|
raise IOError ("Error while copying {0} to {1}".\
|
2012-03-02 16:06:26 +01:00
|
|
|
format(self.clean_img, self.volatile_img))
|
2011-07-26 17:09:59 +02:00
|
|
|
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Copying the template's DispVM prerun script..."
|
2012-03-02 16:06:26 +01:00
|
|
|
retcode = subprocess.call (["cp", src_vm.dir_path + '/dispvm-prerun.sh', self.dir_path + '/dispvm-prerun.sh'])
|
2011-07-26 17:09:59 +02:00
|
|
|
if retcode != 0:
|
|
|
|
raise IOError ("Error while copying DispVM prerun script")
|
|
|
|
|
2011-03-19 03:15:32 +01:00
|
|
|
# Create root-cow.img
|
2011-07-02 13:30:10 +02:00
|
|
|
self.commit_changes(verbose=verbose)
|
2011-03-02 15:00:19 +01:00
|
|
|
|
2011-04-19 13:38:24 +02:00
|
|
|
def create_appmenus(self, verbose, source_template = None):
|
|
|
|
if source_template is None:
|
|
|
|
source_template = self.template_vm
|
|
|
|
|
|
|
|
try:
|
2011-07-10 23:39:48 +02:00
|
|
|
subprocess.check_call ([qubes_appmenu_create_cmd, self.appmenus_templates_dir, self.name, "vm-templates"])
|
2011-04-19 13:38:24 +02:00
|
|
|
except subprocess.CalledProcessError:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "Ooops, there was a problem creating appmenus for {0} VM!".format (self.name)
|
2011-04-19 13:38:24 +02:00
|
|
|
|
2011-11-01 23:42:13 +01:00
|
|
|
def remove_appmenus(self):
|
|
|
|
subprocess.check_call ([qubes_appmenu_remove_cmd, self.name, "vm-templates"])
|
|
|
|
|
2011-11-02 00:39:23 +01:00
|
|
|
def pre_rename(self, new_name):
|
|
|
|
self.remove_appmenus()
|
|
|
|
|
|
|
|
def post_rename(self, old_name):
|
|
|
|
self.create_appmenus(False)
|
|
|
|
|
|
|
|
old_dirpath = os.path.dirname(self.dir_path) + '/' + old_name
|
|
|
|
self.clean_volatile_img = self.clean_volatile_img.replace(old_dirpath, self.dir_path)
|
|
|
|
self.rootcow_img = self.rootcow_img.replace(old_dirpath, self.dir_path)
|
|
|
|
|
2011-04-19 17:55:06 +02:00
|
|
|
def remove_from_disk(self):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2011-11-01 23:42:13 +01:00
|
|
|
self.remove_appmenus()
|
2011-04-19 17:55:06 +02:00
|
|
|
super(QubesTemplateVm, self).remove_from_disk()
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def verify_files(self):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if not os.path.exists (self.dir_path):
|
|
|
|
raise QubesException (
|
|
|
|
"VM directory doesn't exist: {0}".\
|
|
|
|
format(self.dir_path))
|
|
|
|
|
|
|
|
if not os.path.exists (self.root_img):
|
|
|
|
raise QubesException (
|
|
|
|
"VM root image file doesn't exist: {0}".\
|
|
|
|
format(self.root_img))
|
|
|
|
|
|
|
|
if not os.path.exists (self.private_img):
|
|
|
|
raise QubesException (
|
|
|
|
"VM private image file doesn't exist: {0}".\
|
|
|
|
format(self.private_img))
|
|
|
|
|
2011-03-19 03:15:32 +01:00
|
|
|
if not os.path.exists (self.volatile_img):
|
|
|
|
raise QubesException (
|
|
|
|
"VM volatile image file doesn't exist: {0}".\
|
|
|
|
format(self.volatile_img))
|
|
|
|
|
|
|
|
if not os.path.exists (self.clean_volatile_img):
|
|
|
|
raise QubesException (
|
|
|
|
"Clean VM volatile image file doesn't exist: {0}".\
|
|
|
|
format(self.clean_volatile_img))
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
if not os.path.exists (self.kernels_dir):
|
|
|
|
raise QubesException (
|
|
|
|
"VM's kernels directory does not exist: {0}".\
|
|
|
|
format(self.kernels_dir))
|
2011-03-02 15:00:19 +01:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
return True
|
|
|
|
|
2011-03-10 17:24:56 +01:00
|
|
|
def start(self, debug_console = False, verbose = False, preparing_dvm=False):
|
2010-04-05 20:58:57 +02:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2011-07-02 13:30:10 +02:00
|
|
|
self.reset_volatile_storage(verbose=verbose)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
if not self.is_updateable():
|
|
|
|
raise QubesException ("Cannot start Template VM that is marked \"nonupdatable\"")
|
|
|
|
|
2011-02-27 00:06:46 +01:00
|
|
|
# TODO?: check if none of running appvms are outdated
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
return super(QubesTemplateVm, self).start(debug_console=debug_console, verbose=verbose)
|
|
|
|
|
2011-07-02 13:30:10 +02:00
|
|
|
def reset_volatile_storage(self, verbose = False):
|
2011-03-19 03:15:32 +01:00
|
|
|
assert not self.is_running(), "Attempt to clean volatile image of running Template VM!"
|
|
|
|
|
2011-07-02 13:30:10 +02:00
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Cleaning volatile image: {0}...".format (self.volatile_img)
|
2011-03-19 03:15:32 +01:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
if os.path.exists (self.volatile_img):
|
|
|
|
os.remove (self.volatile_img)
|
|
|
|
|
2011-03-19 22:05:00 +01:00
|
|
|
retcode = subprocess.call (["tar", "xf", self.clean_volatile_img, "-C", self.dir_path])
|
2011-03-19 03:15:32 +01:00
|
|
|
if retcode != 0:
|
2011-03-19 22:05:00 +01:00
|
|
|
raise IOError ("Error while unpacking {0} to {1}".\
|
|
|
|
format(self.template_vm.clean_volatile_img, self.volatile_img))
|
2011-03-19 03:15:32 +01:00
|
|
|
|
2011-07-02 13:30:10 +02:00
|
|
|
def commit_changes (self, verbose = False):
|
2011-02-27 00:06:46 +01:00
|
|
|
|
|
|
|
assert not self.is_running(), "Attempt to commit changes on running Template VM!"
|
|
|
|
|
2011-07-02 13:30:10 +02:00
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Commiting template updates... COW: {0}...".format (self.rootcow_img)
|
2011-02-27 00:06:46 +01:00
|
|
|
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
if os.path.exists (self.rootcow_img):
|
2011-06-09 14:22:22 +02:00
|
|
|
os.rename (self.rootcow_img, self.rootcow_img + '.old')
|
2011-02-27 00:06:46 +01:00
|
|
|
|
|
|
|
f_cow = open (self.rootcow_img, "w")
|
|
|
|
f_root = open (self.root_img, "r")
|
|
|
|
f_root.seek(0, os.SEEK_END)
|
|
|
|
f_cow.truncate (f_root.tell()) # make empty sparse file of the same size as root.img
|
|
|
|
f_cow.close ()
|
|
|
|
f_root.close()
|
|
|
|
|
2011-03-17 00:42:01 +01:00
|
|
|
def get_xml_attrs(self):
|
|
|
|
attrs = super(QubesTemplateVm, self).get_xml_attrs()
|
2011-03-19 03:15:32 +01:00
|
|
|
attrs["clean_volatile_img"] = self.clean_volatile_img
|
|
|
|
attrs["rootcow_img"] = self.rootcow_img
|
2011-03-17 00:42:01 +01:00
|
|
|
return attrs
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
class QubesNetVm(QubesVm):
|
2010-04-05 20:58:57 +02:00
|
|
|
"""
|
2011-03-06 17:06:45 +01:00
|
|
|
A class that represents a NetVM. A child of QubesCowVM.
|
2010-04-05 20:58:57 +02:00
|
|
|
"""
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
netid = kwargs.pop("netid")
|
|
|
|
self.netid = netid
|
2011-03-27 12:28:13 +02:00
|
|
|
self.__network = "10.137.{0}.0".format(netid)
|
|
|
|
self.netprefix = "10.137.{0}.".format(netid)
|
2011-07-09 17:52:47 +02:00
|
|
|
self.dispnetprefix = "10.138.{0}.".format(netid)
|
2010-04-05 20:58:57 +02:00
|
|
|
self.__netmask = vm_default_netmask
|
2011-03-27 12:28:13 +02:00
|
|
|
self.__gateway = self.netprefix + "1"
|
|
|
|
self.__secondary_dns = self.netprefix + "254"
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
|
|
|
|
kwargs["dir_path"] = qubes_servicevms_dir + "/" + kwargs["name"]
|
2011-03-09 19:47:12 +01:00
|
|
|
self.__external_ip_allowed_xids = set()
|
2011-03-06 17:06:45 +01:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
if "label" not in kwargs or kwargs["label"] is None:
|
|
|
|
kwargs["label"] = default_servicevm_label
|
2011-03-16 18:39:54 +01:00
|
|
|
|
|
|
|
if "vcpus" not in kwargs or kwargs["vcpus"] is None:
|
|
|
|
kwargs["vcpus"] = default_servicevm_vcpus
|
2011-04-06 13:34:03 +02:00
|
|
|
|
|
|
|
if "memory" not in kwargs or kwargs["memory"] is None:
|
|
|
|
kwargs["memory"] = 200
|
|
|
|
|
2011-06-06 01:46:35 +02:00
|
|
|
kwargs["maxmem"] = kwargs["memory"]
|
|
|
|
|
2011-03-16 11:28:16 +01:00
|
|
|
super(QubesNetVm, self).__init__(**kwargs)
|
2011-04-04 19:08:40 +02:00
|
|
|
self.connected_vms = QubesVmCollection()
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-02-09 21:35:36 +01:00
|
|
|
@property
|
|
|
|
def type(self):
|
|
|
|
return "NetVM"
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
@property
|
|
|
|
def gateway(self):
|
|
|
|
return self.__gateway
|
|
|
|
|
2010-05-19 16:19:01 +02:00
|
|
|
@property
|
|
|
|
def secondary_dns(self):
|
|
|
|
return self.__secondary_dns
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
@property
|
|
|
|
def netmask(self):
|
|
|
|
return self.__netmask
|
|
|
|
|
|
|
|
@property
|
|
|
|
def network(self):
|
|
|
|
return self.__network
|
|
|
|
|
|
|
|
def get_ip_for_vm(self, qid):
|
2011-03-02 15:00:19 +01:00
|
|
|
lo = qid % 253 + 2
|
2011-03-27 12:28:13 +02:00
|
|
|
assert lo >= 2 and lo <= 254, "Wrong IP address for VM"
|
|
|
|
return self.netprefix + "{0}".format(lo)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-07-09 17:52:47 +02:00
|
|
|
def get_ip_for_dispvm(self, dispid):
|
|
|
|
lo = dispid % 254 + 1
|
|
|
|
assert lo >= 1 and lo <= 254, "Wrong IP address for VM"
|
|
|
|
return self.dispnetprefix + "{0}".format(lo)
|
|
|
|
|
2011-09-09 18:49:15 +02:00
|
|
|
def create_xenstore_entries(self, xid = None):
|
2011-03-09 19:47:12 +01:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2011-09-09 18:49:15 +02:00
|
|
|
if xid is None:
|
|
|
|
xid = self.xid
|
|
|
|
|
|
|
|
|
2011-03-09 19:47:12 +01:00
|
|
|
super(QubesNetVm, self).create_xenstore_entries(xid)
|
2011-06-05 23:35:53 +02:00
|
|
|
xs.write('', "/local/domain/{0}/qubes_netvm_external_ip".format(xid), '')
|
2011-03-10 13:38:49 +01:00
|
|
|
self.update_external_ip_permissions(xid)
|
|
|
|
|
|
|
|
def update_external_ip_permissions(self, xid = -1):
|
|
|
|
if xid < 0:
|
|
|
|
xid = self.get_xid()
|
|
|
|
if xid < 0:
|
|
|
|
return
|
2011-03-09 19:47:12 +01:00
|
|
|
|
|
|
|
command = [
|
|
|
|
"/usr/bin/xenstore-chmod",
|
|
|
|
"/local/domain/{0}/qubes_netvm_external_ip".format(xid)
|
|
|
|
]
|
|
|
|
|
2011-09-26 17:24:11 +02:00
|
|
|
command.append("n{0}".format(xid))
|
2011-03-09 19:47:12 +01:00
|
|
|
|
|
|
|
for id in self.__external_ip_allowed_xids:
|
|
|
|
command.append("r{0}".format(id))
|
|
|
|
|
|
|
|
return subprocess.check_call(command)
|
|
|
|
|
2011-04-23 02:32:54 +02:00
|
|
|
def start(self, debug_console = False, verbose = False, preparing_dvm=False):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
xid=super(QubesNetVm, self).start(debug_console=debug_console, verbose=verbose)
|
|
|
|
|
|
|
|
# Connect vif's of already running VMs
|
|
|
|
for vm in self.connected_vms.values():
|
|
|
|
if not vm.is_running():
|
|
|
|
continue
|
|
|
|
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Attaching network to '{0}'...".format(vm.name)
|
2011-04-23 02:32:54 +02:00
|
|
|
|
|
|
|
# Cleanup stale VIFs
|
|
|
|
vm.cleanup_vifs()
|
|
|
|
|
2011-09-03 16:07:10 +02:00
|
|
|
# wait for frontend to forget about this device (UGLY HACK)
|
2011-10-10 17:11:00 +02:00
|
|
|
time.sleep(0.2)
|
2011-09-03 16:07:10 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
vm.attach_network(wait=False)
|
|
|
|
except QubesException as ex:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, ("WARNING: Cannot attach to network to '{0}': {1}".format(vm.name, ex))
|
2011-09-03 16:07:10 +02:00
|
|
|
|
2011-04-23 02:32:54 +02:00
|
|
|
return xid
|
|
|
|
|
2011-10-17 22:15:10 +02:00
|
|
|
def shutdown(self, force=False):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
connected_vms = [vm for vm in self.connected_vms.values() if vm.is_running()]
|
|
|
|
if connected_vms and not force:
|
|
|
|
raise QubesException("There are other VMs connected to this VM: " + str([vm.name for vm in connected_vms]))
|
|
|
|
|
|
|
|
super(QubesNetVm, self).shutdown(force=force)
|
|
|
|
|
2011-03-09 19:47:12 +01:00
|
|
|
def add_external_ip_permission(self, xid):
|
|
|
|
if int(xid) < 0:
|
|
|
|
return
|
|
|
|
self.__external_ip_allowed_xids.add(int(xid))
|
|
|
|
self.update_external_ip_permissions()
|
|
|
|
|
|
|
|
def remove_external_ip_permission(self, xid):
|
|
|
|
self.__external_ip_allowed_xids.discard(int(xid))
|
|
|
|
self.update_external_ip_permissions()
|
|
|
|
|
2011-03-17 00:42:01 +01:00
|
|
|
def get_xml_attrs(self):
|
|
|
|
attrs = super(QubesNetVm, self).get_xml_attrs()
|
|
|
|
attrs.pop("netvm_qid")
|
|
|
|
attrs.pop("uses_default_netvm")
|
|
|
|
attrs["netid"] = str(self.netid)
|
|
|
|
return attrs
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
class QubesProxyVm(QubesNetVm):
|
2011-02-09 21:21:14 +01:00
|
|
|
"""
|
2011-03-06 17:06:45 +01:00
|
|
|
A class that represents a ProxyVM, ex FirewallVM. A child of QubesNetVM.
|
2011-02-09 21:21:14 +01:00
|
|
|
"""
|
|
|
|
def __init__(self, **kwargs):
|
2011-03-17 01:40:15 +01:00
|
|
|
kwargs["uses_default_netvm"] = False
|
|
|
|
super(QubesProxyVm, self).__init__(**kwargs)
|
2011-03-09 18:07:22 +01:00
|
|
|
self.rules_applied = None
|
2011-02-09 21:35:36 +01:00
|
|
|
|
|
|
|
@property
|
|
|
|
def type(self):
|
2011-03-06 17:06:45 +01:00
|
|
|
return "ProxyVM"
|
2011-02-09 21:21:14 +01:00
|
|
|
|
2011-03-10 13:38:49 +01:00
|
|
|
def start(self, debug_console = False, verbose = False, preparing_dvm = False):
|
|
|
|
if dry_run:
|
|
|
|
return
|
2011-03-15 17:40:23 +01:00
|
|
|
retcode = super(QubesProxyVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm)
|
2012-03-04 21:59:02 +01:00
|
|
|
self.netvm.add_external_ip_permission(self.get_xid())
|
2011-03-10 13:38:49 +01:00
|
|
|
self.write_netvm_domid_entry()
|
|
|
|
return retcode
|
|
|
|
|
2011-03-09 19:47:12 +01:00
|
|
|
def force_shutdown(self):
|
|
|
|
if dry_run:
|
|
|
|
return
|
2012-03-04 21:59:02 +01:00
|
|
|
self.netvm.remove_external_ip_permission(self.get_xid())
|
2011-03-15 17:40:23 +01:00
|
|
|
super(QubesProxyVm, self).force_shutdown()
|
2011-03-09 19:47:12 +01:00
|
|
|
|
2011-09-09 18:49:15 +02:00
|
|
|
def create_xenstore_entries(self, xid = None):
|
2011-02-21 18:13:27 +01:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2011-09-09 18:49:15 +02:00
|
|
|
if xid is None:
|
|
|
|
xid = self.xid
|
|
|
|
|
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
super(QubesProxyVm, self).create_xenstore_entries(xid)
|
2011-06-25 22:31:22 +02:00
|
|
|
xs.write('', "/local/domain/{0}/qubes_iptables_error".format(xid), '')
|
|
|
|
xs.set_permissions('', "/local/domain/{0}/qubes_iptables_error".format(xid),
|
2011-06-05 23:35:53 +02:00
|
|
|
[{ 'dom': xid, 'write': True }])
|
2011-02-21 18:13:27 +01:00
|
|
|
self.write_iptables_xenstore_entry()
|
|
|
|
|
2011-03-10 13:38:49 +01:00
|
|
|
def write_netvm_domid_entry(self, xid = -1):
|
|
|
|
if xid < 0:
|
|
|
|
xid = self.get_xid()
|
|
|
|
|
2011-06-05 23:35:53 +02:00
|
|
|
xs.write('', "/local/domain/{0}/qubes_netvm_domid".format(xid),
|
2012-03-04 21:59:02 +01:00
|
|
|
"{0}".format(self.netvm.get_xid()))
|
2011-03-10 13:38:49 +01:00
|
|
|
|
2011-02-21 18:13:27 +01:00
|
|
|
def write_iptables_xenstore_entry(self):
|
2011-07-29 16:50:12 +02:00
|
|
|
xs.rm('', "/local/domain/{0}/qubes_iptables_domainrules".format(self.get_xid()))
|
2011-03-02 15:03:21 +01:00
|
|
|
iptables = "# Generated by Qubes Core on {0}\n".format(datetime.now().ctime())
|
|
|
|
iptables += "*filter\n"
|
|
|
|
iptables += ":INPUT DROP [0:0]\n"
|
|
|
|
iptables += ":FORWARD DROP [0:0]\n"
|
|
|
|
iptables += ":OUTPUT ACCEPT [0:0]\n"
|
|
|
|
|
|
|
|
# Strict INPUT rules
|
|
|
|
iptables += "-A INPUT -i vif+ -p udp -m udp --dport 68 -j DROP\n"
|
|
|
|
iptables += "-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
|
|
|
|
iptables += "-A INPUT -p icmp -j ACCEPT\n"
|
|
|
|
iptables += "-A INPUT -i lo -j ACCEPT\n"
|
|
|
|
iptables += "-A INPUT -j REJECT --reject-with icmp-host-prohibited\n"
|
|
|
|
|
2011-04-06 10:32:20 +02:00
|
|
|
iptables += "-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
|
2011-03-02 15:03:21 +01:00
|
|
|
# Allow dom0 networking
|
|
|
|
iptables += "-A FORWARD -i vif0.0 -j ACCEPT\n"
|
2011-04-06 10:32:20 +02:00
|
|
|
# Deny inter-VMs networking
|
|
|
|
iptables += "-A FORWARD -i vif+ -o vif+ -j DROP\n"
|
2011-07-29 16:50:12 +02:00
|
|
|
iptables += "COMMIT\n"
|
|
|
|
xs.write('', "/local/domain/{0}/qubes_iptables_header".format(self.get_xid()), iptables)
|
2011-03-02 15:03:21 +01:00
|
|
|
|
2011-04-05 14:39:40 +02:00
|
|
|
vms = [vm for vm in self.connected_vms.values()]
|
2011-03-02 15:03:21 +01:00
|
|
|
for vm in vms:
|
2011-07-29 16:50:12 +02:00
|
|
|
iptables="*filter\n"
|
2011-04-04 17:07:46 +02:00
|
|
|
if vm.has_firewall():
|
|
|
|
conf = vm.get_firewall_conf()
|
|
|
|
else:
|
|
|
|
conf = { "rules": list(), "allow": True, "allowDns": True, "allowIcmp": True }
|
2011-03-02 15:03:21 +01:00
|
|
|
|
|
|
|
xid = vm.get_xid()
|
|
|
|
if xid < 0: # VM not active ATM
|
|
|
|
continue
|
|
|
|
|
2012-03-02 01:16:03 +01:00
|
|
|
vif = vm.vif
|
|
|
|
if vif is None:
|
|
|
|
continue
|
|
|
|
|
2011-03-02 15:03:21 +01:00
|
|
|
iptables += "# '{0}' VM:\n".format(vm.name)
|
2012-03-02 01:16:03 +01:00
|
|
|
iptables += "-A FORWARD ! -s {0}/32 -i {1} -j DROP\n".format(vm.ip, vif)
|
2011-03-02 15:03:21 +01:00
|
|
|
|
2011-03-03 22:40:36 +01:00
|
|
|
accept_action = "ACCEPT"
|
|
|
|
reject_action = "REJECT --reject-with icmp-host-prohibited"
|
2011-03-02 15:03:21 +01:00
|
|
|
|
2011-03-03 22:40:36 +01:00
|
|
|
if conf["allow"]:
|
|
|
|
default_action = accept_action
|
2011-03-18 14:12:19 +01:00
|
|
|
rules_action = reject_action
|
2011-03-03 22:40:36 +01:00
|
|
|
else:
|
2011-03-18 14:12:19 +01:00
|
|
|
default_action = reject_action
|
|
|
|
rules_action = accept_action
|
2011-03-02 15:03:21 +01:00
|
|
|
|
2011-03-03 22:40:36 +01:00
|
|
|
for rule in conf["rules"]:
|
2012-03-02 01:16:03 +01:00
|
|
|
iptables += "-A FORWARD -i {0} -d {1}".format(vif, rule["address"])
|
2011-03-03 22:40:36 +01:00
|
|
|
if rule["netmask"] != 32:
|
|
|
|
iptables += "/{0}".format(rule["netmask"])
|
2011-03-02 15:03:21 +01:00
|
|
|
|
2012-02-27 02:02:42 +01:00
|
|
|
if rule["proto"] is not None and rule["proto"] != "any":
|
|
|
|
iptables += " -p {0}".format(rule["proto"])
|
|
|
|
if rule["portBegin"] is not None and rule["portBegin"] > 0:
|
|
|
|
iptables += " --dport {0}".format(rule["portBegin"])
|
|
|
|
if rule["portEnd"] is not None and rule["portEnd"] > rule["portBegin"]:
|
|
|
|
iptables += ":{0}".format(rule["portEnd"])
|
2011-03-02 15:03:21 +01:00
|
|
|
|
2011-03-03 22:40:36 +01:00
|
|
|
iptables += " -j {0}\n".format(rules_action)
|
2011-03-02 15:03:21 +01:00
|
|
|
|
2011-03-03 22:40:36 +01:00
|
|
|
if conf["allowDns"]:
|
2012-03-04 21:59:02 +01:00
|
|
|
# PREROUTING does DNAT to NetVM DNSes, so we need self.netvm. properties
|
|
|
|
iptables += "-A FORWARD -i {0} -p udp -d {1} --dport 53 -j ACCEPT\n".format(vif,self.netvm.gateway)
|
|
|
|
iptables += "-A FORWARD -i {0} -p udp -d {1} --dport 53 -j ACCEPT\n".format(vif,self.netvm.secondary_dns)
|
2011-03-21 22:06:53 +01:00
|
|
|
if conf["allowIcmp"]:
|
2012-03-02 01:16:03 +01:00
|
|
|
iptables += "-A FORWARD -i {0} -p icmp -j ACCEPT\n".format(vif)
|
2011-03-02 15:03:21 +01:00
|
|
|
|
2012-03-02 01:16:03 +01:00
|
|
|
iptables += "-A FORWARD -i {0} -j {1}\n".format(vif, default_action)
|
2011-07-29 16:50:12 +02:00
|
|
|
iptables += "COMMIT\n"
|
|
|
|
xs.write('', "/local/domain/"+str(self.get_xid())+"/qubes_iptables_domainrules/"+str(xid), iptables)
|
|
|
|
# no need for ending -A FORWARD -j DROP, cause default action is DROP
|
2011-03-02 15:03:21 +01:00
|
|
|
|
2011-03-10 13:38:49 +01:00
|
|
|
self.write_netvm_domid_entry()
|
|
|
|
|
2011-03-09 18:07:22 +01:00
|
|
|
self.rules_applied = None
|
2011-07-29 16:50:12 +02:00
|
|
|
xs.write('', "/local/domain/{0}/qubes_iptables".format(self.get_xid()), 'reload')
|
2011-02-21 18:13:27 +01:00
|
|
|
|
2011-03-17 00:42:01 +01:00
|
|
|
def get_xml_attrs(self):
|
|
|
|
attrs = super(QubesProxyVm, self).get_xml_attrs()
|
2012-03-04 21:59:02 +01:00
|
|
|
attrs["netvm_qid"] = str(self.netvm.qid) if self.netvm is not None else "none"
|
2011-03-17 00:42:01 +01:00
|
|
|
return attrs
|
2011-02-09 21:21:14 +01:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
class QubesDom0NetVm(QubesNetVm):
|
|
|
|
def __init__(self):
|
|
|
|
super(QubesDom0NetVm, self).__init__(qid=0, name="dom0", netid=0,
|
2011-03-06 17:06:45 +01:00
|
|
|
dir_path=None,
|
2010-04-05 20:58:57 +02:00
|
|
|
private_img = None,
|
2011-03-06 17:06:45 +01:00
|
|
|
template_vm = None,
|
2010-04-05 20:58:57 +02:00
|
|
|
label = default_template_label)
|
2011-06-02 01:20:01 +02:00
|
|
|
self.xid = 0
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
def is_running(self):
|
|
|
|
return True
|
|
|
|
|
|
|
|
def get_xid(self):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def get_power_state(self):
|
|
|
|
return "Running"
|
|
|
|
|
|
|
|
def get_disk_usage(self, file_or_dir):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def get_disk_utilization(self):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def get_disk_utilization_private_img(self):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def get_private_img_sz(self):
|
|
|
|
return 0
|
|
|
|
|
2011-09-03 16:07:10 +02:00
|
|
|
@property
|
|
|
|
def ip(self):
|
|
|
|
return "10.137.0.1"
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def start(self, debug_console = False, verbose = False):
|
|
|
|
raise QubesException ("Cannot start Dom0 fake domain!")
|
|
|
|
|
2011-06-02 01:20:23 +02:00
|
|
|
def get_xl_dominfo(self):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
domains = xl_ctx.list_domains()
|
|
|
|
for dominfo in domains:
|
|
|
|
if dominfo.domid == 0:
|
|
|
|
return dominfo
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_xc_dominfo(self):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
domains = xc.domain_getinfo(0, 1)
|
|
|
|
return domains[0]
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def create_xml_element(self):
|
|
|
|
return None
|
|
|
|
|
|
|
|
def verify_files(self):
|
|
|
|
return True
|
|
|
|
|
2010-09-21 15:59:22 +02:00
|
|
|
class QubesDisposableVm(QubesVm):
|
|
|
|
"""
|
2011-03-06 17:06:45 +01:00
|
|
|
A class that represents an DisposableVM. A child of QubesVm.
|
2010-09-21 15:59:22 +02:00
|
|
|
"""
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
template_vm = kwargs["template_vm"]
|
2010-09-21 15:59:22 +02:00
|
|
|
assert template_vm is not None, "Missing template_vm for DisposableVM!"
|
|
|
|
|
2011-07-09 17:52:47 +02:00
|
|
|
self.dispid = kwargs.pop("dispid")
|
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
super(QubesDisposableVm, self).__init__(dir_path="/nonexistent", **kwargs)
|
2010-09-21 15:59:22 +02:00
|
|
|
|
2011-02-09 21:35:36 +01:00
|
|
|
@property
|
|
|
|
def type(self):
|
|
|
|
return "DisposableVM"
|
|
|
|
|
2011-07-09 17:52:47 +02:00
|
|
|
@property
|
|
|
|
def ip(self):
|
2012-03-04 21:59:02 +01:00
|
|
|
if self.netvm is not None:
|
|
|
|
return self.netvm.get_ip_for_dispvm(self.dispid)
|
2011-07-09 17:52:47 +02:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
2011-03-17 00:42:01 +01:00
|
|
|
def get_xml_attrs(self):
|
|
|
|
attrs = {}
|
|
|
|
attrs["qid"] = str(self.qid)
|
|
|
|
attrs["name"] = self.name
|
2011-07-09 17:52:47 +02:00
|
|
|
attrs["dispid"] = str(self.dispid)
|
2011-03-17 00:42:01 +01:00
|
|
|
attrs["template_qid"] = str(self.template_vm.qid)
|
|
|
|
attrs["label"] = self.label.name
|
2011-11-01 23:31:37 +01:00
|
|
|
attrs["firewall_conf"] = self.relative_path(self.firewall_conf)
|
2011-03-17 00:42:01 +01:00
|
|
|
return attrs
|
2010-09-21 15:59:22 +02:00
|
|
|
|
|
|
|
def verify_files(self):
|
|
|
|
return True
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
class QubesAppVm(QubesVm):
|
2010-04-05 20:58:57 +02:00
|
|
|
"""
|
2011-03-06 17:06:45 +01:00
|
|
|
A class that represents an AppVM. A child of QubesVm.
|
2010-04-05 20:58:57 +02:00
|
|
|
"""
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
|
|
|
|
kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"]
|
2011-02-21 18:13:27 +01:00
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
super(QubesAppVm, self).__init__(**kwargs)
|
2011-02-21 18:13:27 +01:00
|
|
|
|
2011-02-09 21:35:36 +01:00
|
|
|
@property
|
|
|
|
def type(self):
|
|
|
|
return "AppVM"
|
|
|
|
|
2011-04-04 18:41:02 +02:00
|
|
|
def create_on_disk(self, verbose, source_template = None):
|
2010-09-27 16:53:17 +02:00
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2011-04-04 18:41:02 +02:00
|
|
|
super(QubesAppVm, self).create_on_disk(verbose, source_template=source_template)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-09-06 01:15:35 +02:00
|
|
|
if self.is_updateable():
|
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}".\
|
2011-09-06 01:15:35 +02:00
|
|
|
format(source_template.appmenus_templates_dir, self.appmenus_templates_dir)
|
|
|
|
shutil.copytree (source_template.appmenus_templates_dir, self.appmenus_templates_dir)
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
if verbose:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path)
|
2010-05-11 16:00:16 +02:00
|
|
|
os.symlink (self.label.icon_path, self.icon_path)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-04-03 17:47:20 +02:00
|
|
|
if not self.internal:
|
2011-04-04 18:41:02 +02:00
|
|
|
self.create_appmenus (verbose, source_template=source_template)
|
2010-06-26 15:00:19 +02:00
|
|
|
|
2011-04-01 01:14:18 +02:00
|
|
|
def remove_from_disk(self):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
2011-11-01 23:42:13 +01:00
|
|
|
self.remove_appmenus()
|
2011-04-01 01:14:18 +02:00
|
|
|
super(QubesAppVm, self).remove_from_disk()
|
|
|
|
|
2011-11-02 00:39:23 +01:00
|
|
|
def pre_rename(self, new_name):
|
|
|
|
self.remove_appmenus()
|
|
|
|
|
|
|
|
def post_rename(self, old_name):
|
|
|
|
self.create_appmenus(False)
|
|
|
|
|
2012-02-24 04:23:27 +01:00
|
|
|
class QubesHVm(QubesVm):
|
|
|
|
"""
|
|
|
|
A class that represents an HVM. A child of QubesVm.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# FIXME: logically should inherit after QubesAppVm, but none of its methods
|
|
|
|
# are useful for HVM
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
|
|
|
|
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
|
|
|
|
kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"]
|
|
|
|
|
2012-03-02 01:55:16 +01:00
|
|
|
# only updateable HVM supported
|
|
|
|
kwargs["updateable"] = True
|
|
|
|
kwargs["template_vm"] = None
|
2012-03-02 14:28:09 +01:00
|
|
|
if "memory" not in kwargs or kwargs["memory"] is None:
|
|
|
|
kwargs["memory"] = default_hvm_memory
|
2012-03-02 01:55:16 +01:00
|
|
|
|
2012-02-24 04:23:27 +01:00
|
|
|
super(QubesHVm, self).__init__(**kwargs)
|
|
|
|
self.updateable = True
|
|
|
|
self.config_file_template = config_template_hvm
|
2012-03-02 12:04:57 +01:00
|
|
|
self.private_img = None
|
|
|
|
self.volatile_img = None
|
2012-02-24 04:23:27 +01:00
|
|
|
# remove settings not used by HVM (at least for now)
|
|
|
|
self.__delattr__('kernel')
|
|
|
|
self.__delattr__('kernelopts')
|
|
|
|
self.__delattr__('uses_default_kernel')
|
|
|
|
self.__delattr__('uses_default_kernelopts')
|
|
|
|
# HVM doesn't support dynamic memory management
|
|
|
|
self.maxmem = self.memory
|
|
|
|
|
|
|
|
self.drive = None
|
|
|
|
if 'drive' in kwargs.keys():
|
|
|
|
self.drive = kwargs['drive']
|
|
|
|
|
|
|
|
@property
|
|
|
|
def type(self):
|
|
|
|
return "HVM"
|
|
|
|
|
|
|
|
def is_appvm(self):
|
|
|
|
return True
|
|
|
|
|
2012-03-02 16:28:15 +01:00
|
|
|
def get_clone_attrs(self):
|
|
|
|
attrs = super(QubesHVm, self).get_clone_attrs()
|
|
|
|
attrs.remove('kernel')
|
|
|
|
attrs.remove('uses_default_kernel')
|
|
|
|
attrs.remove('kernelopts')
|
|
|
|
attrs.remove('uses_default_kernelopts')
|
|
|
|
attrs.remove('maxmem')
|
|
|
|
return attrs
|
|
|
|
|
2012-02-24 04:23:27 +01:00
|
|
|
def create_on_disk(self, verbose, source_template = None):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path)
|
|
|
|
os.mkdir (self.dir_path)
|
|
|
|
|
2012-03-02 01:55:30 +01:00
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path)
|
|
|
|
os.symlink (self.label.icon_path, self.icon_path)
|
|
|
|
|
2012-03-02 01:46:10 +01:00
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Creating appmenus directory: {0}".format(self.appmenus_templates_dir)
|
|
|
|
os.mkdir (self.appmenus_templates_dir)
|
|
|
|
shutil.copy (start_appmenu_template, self.appmenus_templates_dir)
|
|
|
|
|
|
|
|
if not self.internal:
|
|
|
|
self.create_appmenus (verbose, source_template=source_template)
|
|
|
|
|
2012-02-24 04:23:27 +01:00
|
|
|
self.create_config_file()
|
|
|
|
|
|
|
|
# create empty disk
|
|
|
|
f_root = open(self.root_img, "w")
|
|
|
|
f_root.truncate(default_hvm_disk_size)
|
|
|
|
f_root.close()
|
|
|
|
|
|
|
|
|
|
|
|
def get_disk_utilization_private_img(self):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def get_private_img_sz(self):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def resize_private_img(self, size):
|
|
|
|
raise NotImplementedError("HVM has no private.img")
|
|
|
|
|
|
|
|
def get_config_params(self, source_template=None):
|
|
|
|
|
|
|
|
params = super(QubesHVm, self).get_config_params(source_template=source_template)
|
|
|
|
|
|
|
|
params['volatiledev'] = ''
|
|
|
|
params['privatedev'] = ''
|
|
|
|
if self.drive:
|
|
|
|
stat_res = os.stat(self.drive)
|
|
|
|
if stat.S_ISBLK(stat_res.st_mode):
|
2012-03-01 11:23:07 +01:00
|
|
|
params['otherdevs'] = "'phy:%s,xvdc:cdrom,r'," % self.drive
|
2012-02-24 04:23:27 +01:00
|
|
|
else:
|
2012-03-01 11:23:07 +01:00
|
|
|
params['otherdevs'] = "'script:file:%s,xvdc:cdrom,r'," % self.drive
|
2012-02-24 04:23:27 +01:00
|
|
|
else:
|
|
|
|
params['otherdevs'] = ''
|
|
|
|
return params
|
|
|
|
|
|
|
|
def verify_files(self):
|
|
|
|
if dry_run:
|
|
|
|
return
|
|
|
|
|
|
|
|
if not os.path.exists (self.dir_path):
|
|
|
|
raise QubesException (
|
|
|
|
"VM directory doesn't exist: {0}".\
|
|
|
|
format(self.dir_path))
|
|
|
|
|
|
|
|
if self.is_updateable() and not os.path.exists (self.root_img):
|
|
|
|
raise QubesException (
|
|
|
|
"VM root image file doesn't exist: {0}".\
|
|
|
|
format(self.root_img))
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
def reset_volatile_storage(self, **kwargs):
|
|
|
|
pass
|
|
|
|
|
2012-03-02 01:21:40 +01:00
|
|
|
@property
|
|
|
|
def vif(self):
|
2012-03-02 02:27:13 +01:00
|
|
|
if self.xid < 0:
|
2012-03-02 01:21:40 +01:00
|
|
|
return None
|
|
|
|
if self.netvm_vm is None:
|
|
|
|
return None
|
|
|
|
return "vif{0}.+".format(self.stubdom_xid)
|
|
|
|
|
2012-02-24 04:23:27 +01:00
|
|
|
def run(self, command, **kwargs):
|
|
|
|
raise NotImplementedError("Needs qrexec agent - TODO")
|
|
|
|
|
|
|
|
@property
|
|
|
|
def stubdom_xid(self):
|
2012-03-02 02:38:01 +01:00
|
|
|
if self.xid < 0:
|
2012-02-24 04:23:27 +01:00
|
|
|
return -1
|
|
|
|
|
|
|
|
return int(xs.read('', '/local/domain/%d/image/device-model-domid' % self.xid))
|
|
|
|
|
|
|
|
def start_guid(self, verbose = True, notify_function = None):
|
|
|
|
if verbose:
|
|
|
|
print >> sys.stderr, "--> Starting Qubes GUId..."
|
|
|
|
|
|
|
|
retcode = subprocess.call ([qubes_guid_path, "-d", str(self.stubdom_xid), "-c", self.label.color, "-i", self.label.icon, "-l", str(self.label.index)])
|
|
|
|
if (retcode != 0) :
|
|
|
|
raise QubesException("Cannot start qubes_guid!")
|
|
|
|
|
|
|
|
def start_qrexec_daemon(self, **kwargs):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def get_xml_attrs(self):
|
|
|
|
attrs = super(QubesHVm, self).get_xml_attrs()
|
|
|
|
attrs["drive"] = str(self.drive)
|
|
|
|
return attrs
|
2011-02-21 18:13:27 +01:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
class QubesVmCollection(dict):
|
|
|
|
"""
|
|
|
|
A collection of Qubes VMs indexed by Qubes id (qid)
|
|
|
|
"""
|
|
|
|
|
2010-06-26 15:00:19 +02:00
|
|
|
def __init__(self, store_filename=qubes_store_filename):
|
2010-04-05 20:58:57 +02:00
|
|
|
super(QubesVmCollection, self).__init__()
|
|
|
|
self.default_netvm_qid = None
|
2011-02-09 21:21:14 +01:00
|
|
|
self.default_fw_netvm_qid = None
|
2010-04-05 20:58:57 +02:00
|
|
|
self.default_template_qid = None
|
2011-07-15 12:24:27 +02:00
|
|
|
self.default_kernel = None
|
2011-06-22 00:44:48 +02:00
|
|
|
self.updatevm_qid = None
|
2010-06-26 15:00:19 +02:00
|
|
|
self.qubes_store_filename = store_filename
|
2011-10-01 02:52:56 +02:00
|
|
|
self.clockvm_qid = None
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
def values(self):
|
|
|
|
for qid in self.keys():
|
|
|
|
yield self[qid]
|
|
|
|
|
|
|
|
def items(self):
|
|
|
|
for qid in self.keys():
|
|
|
|
yield (qid, self[qid])
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
for qid in sorted(super(QubesVmCollection, self).keys()):
|
|
|
|
yield qid
|
|
|
|
|
|
|
|
keys = __iter__
|
|
|
|
|
|
|
|
def __setitem__(self, key, value):
|
|
|
|
if key not in self:
|
|
|
|
return super(QubesVmCollection, self).__setitem__(key, value)
|
|
|
|
else:
|
|
|
|
assert False, "Attempt to add VM with qid that already exists in the collection!"
|
|
|
|
|
|
|
|
|
|
|
|
def add_new_appvm(self, name, template_vm,
|
|
|
|
dir_path = None, conf_file = None,
|
|
|
|
private_img = None,
|
2011-03-16 23:45:02 +01:00
|
|
|
updateable = False,
|
2010-04-05 20:58:57 +02:00
|
|
|
label = None):
|
|
|
|
|
|
|
|
qid = self.get_new_unused_qid()
|
|
|
|
vm = QubesAppVm (qid=qid, name=name, template_vm=template_vm,
|
|
|
|
dir_path=dir_path, conf_file=conf_file,
|
|
|
|
private_img=private_img,
|
2012-03-04 21:59:02 +01:00
|
|
|
netvm = self.get_default_netvm(),
|
2011-07-02 18:48:17 +02:00
|
|
|
kernel = self.get_default_kernel(),
|
|
|
|
uses_default_kernel = True,
|
2011-03-16 23:45:02 +01:00
|
|
|
updateable=updateable,
|
2010-04-05 20:58:57 +02:00
|
|
|
label=label)
|
|
|
|
|
|
|
|
if not self.verify_new_vm (vm):
|
|
|
|
assert False, "Wrong VM description!"
|
|
|
|
self[vm.qid]=vm
|
|
|
|
return vm
|
|
|
|
|
2012-02-24 04:23:27 +01:00
|
|
|
def add_new_hvm(self, name, label = None):
|
|
|
|
|
|
|
|
qid = self.get_new_unused_qid()
|
|
|
|
vm = QubesHVm (qid=qid, name=name,
|
|
|
|
netvm_vm = self.get_default_netvm_vm(),
|
|
|
|
kernel = self.get_default_kernel(),
|
|
|
|
uses_default_kernel = True,
|
|
|
|
label=label)
|
|
|
|
|
|
|
|
if not self.verify_new_vm (vm):
|
|
|
|
assert False, "Wrong VM description!"
|
|
|
|
self[vm.qid]=vm
|
|
|
|
return vm
|
|
|
|
|
2011-07-09 17:52:47 +02:00
|
|
|
def add_new_disposablevm(self, name, template_vm, dispid,
|
2010-09-21 15:59:22 +02:00
|
|
|
label = None):
|
|
|
|
|
|
|
|
qid = self.get_new_unused_qid()
|
|
|
|
vm = QubesDisposableVm (qid=qid, name=name, template_vm=template_vm,
|
2012-03-04 21:59:02 +01:00
|
|
|
netvm = self.get_default_netvm(),
|
2011-07-09 17:52:47 +02:00
|
|
|
label=label, dispid=dispid)
|
2010-09-21 15:59:22 +02:00
|
|
|
|
|
|
|
if not self.verify_new_vm (vm):
|
|
|
|
assert False, "Wrong VM description!"
|
|
|
|
self[vm.qid]=vm
|
|
|
|
return vm
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def add_new_templatevm(self, name,
|
|
|
|
dir_path = None, conf_file = None,
|
|
|
|
root_img = None, private_img = None,
|
|
|
|
installed_by_rpm = True):
|
|
|
|
|
|
|
|
qid = self.get_new_unused_qid()
|
2011-03-02 15:00:19 +01:00
|
|
|
vm = QubesTemplateVm (qid=qid, name=name,
|
2010-04-05 20:58:57 +02:00
|
|
|
dir_path=dir_path, conf_file=conf_file,
|
|
|
|
root_img=root_img, private_img=private_img,
|
|
|
|
installed_by_rpm=installed_by_rpm,
|
2012-03-04 21:59:02 +01:00
|
|
|
netvm = self.get_default_netvm(),
|
2011-07-02 18:48:17 +02:00
|
|
|
kernel = self.get_default_kernel(),
|
|
|
|
uses_default_kernel = True)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
if not self.verify_new_vm (vm):
|
|
|
|
assert False, "Wrong VM description!"
|
|
|
|
self[vm.qid]=vm
|
|
|
|
|
|
|
|
if self.default_template_qid is None:
|
|
|
|
self.set_default_template_vm(vm)
|
|
|
|
|
|
|
|
return vm
|
|
|
|
|
|
|
|
def clone_templatevm(self, src_template_vm, name, dir_path = None, verbose = False):
|
|
|
|
|
|
|
|
assert not src_template_vm.is_running(), "Attempt to clone a running Template VM!"
|
|
|
|
|
|
|
|
vm = self.add_new_templatevm (name=name, dir_path=dir_path, installed_by_rpm = False)
|
|
|
|
|
|
|
|
return vm
|
|
|
|
|
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
def add_new_netvm(self, name, template_vm,
|
2010-04-05 20:58:57 +02:00
|
|
|
dir_path = None, conf_file = None,
|
2011-03-16 11:28:16 +01:00
|
|
|
private_img = None, installed_by_rpm = False,
|
2011-03-16 23:45:02 +01:00
|
|
|
label = None, updateable = False):
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
qid = self.get_new_unused_qid()
|
|
|
|
netid = self.get_new_unused_netid()
|
2011-03-06 17:06:45 +01:00
|
|
|
vm = QubesNetVm (qid=qid, name=name, template_vm=template_vm,
|
2011-03-15 18:28:28 +01:00
|
|
|
netid=netid, label=label,
|
2011-03-16 11:28:16 +01:00
|
|
|
private_img=private_img, installed_by_rpm=installed_by_rpm,
|
2011-03-16 23:45:02 +01:00
|
|
|
updateable=updateable,
|
2011-07-02 18:48:17 +02:00
|
|
|
kernel = self.get_default_kernel(),
|
|
|
|
uses_default_kernel = True,
|
2011-03-11 01:59:56 +01:00
|
|
|
dir_path=dir_path, conf_file=conf_file)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
if not self.verify_new_vm (vm):
|
|
|
|
assert False, "Wrong VM description!"
|
|
|
|
self[vm.qid]=vm
|
|
|
|
|
2011-02-09 21:21:14 +01:00
|
|
|
if self.default_fw_netvm_qid is None:
|
2012-03-04 21:59:02 +01:00
|
|
|
self.set_default_fw_netvm(vm)
|
2011-02-09 21:21:14 +01:00
|
|
|
|
2011-10-17 23:20:21 +02:00
|
|
|
# by default ClockVM is the first NetVM
|
|
|
|
if self.clockvm_qid is None:
|
|
|
|
self.set_clockvm_vm(vm)
|
|
|
|
|
2011-02-09 21:21:14 +01:00
|
|
|
return vm
|
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
def add_new_proxyvm(self, name, template_vm,
|
2011-02-09 21:21:14 +01:00
|
|
|
dir_path = None, conf_file = None,
|
2011-03-16 11:28:16 +01:00
|
|
|
private_img = None, installed_by_rpm = False,
|
2011-03-16 23:45:02 +01:00
|
|
|
label = None, updateable = False):
|
2011-02-09 21:21:14 +01:00
|
|
|
|
|
|
|
qid = self.get_new_unused_qid()
|
|
|
|
netid = self.get_new_unused_netid()
|
2011-03-06 17:06:45 +01:00
|
|
|
vm = QubesProxyVm (qid=qid, name=name, template_vm=template_vm,
|
2011-03-15 18:28:28 +01:00
|
|
|
netid=netid, label=label,
|
2011-03-16 11:28:16 +01:00
|
|
|
private_img=private_img, installed_by_rpm=installed_by_rpm,
|
2011-02-09 21:21:14 +01:00
|
|
|
dir_path=dir_path, conf_file=conf_file,
|
2011-03-16 23:45:02 +01:00
|
|
|
updateable=updateable,
|
2011-07-02 18:48:17 +02:00
|
|
|
kernel = self.get_default_kernel(),
|
|
|
|
uses_default_kernel = True,
|
2012-03-04 21:59:02 +01:00
|
|
|
netvm = self.get_default_fw_netvm())
|
2011-02-09 21:21:14 +01:00
|
|
|
|
|
|
|
if not self.verify_new_vm (vm):
|
|
|
|
assert False, "Wrong VM description!"
|
|
|
|
self[vm.qid]=vm
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
if self.default_netvm_qid is None:
|
2012-03-04 21:59:02 +01:00
|
|
|
self.set_default_netvm(vm)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-06-26 00:16:48 +02:00
|
|
|
if self.updatevm_qid is None:
|
|
|
|
self.set_updatevm_vm(vm)
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
return vm
|
|
|
|
|
|
|
|
def set_default_template_vm(self, vm):
|
2011-03-11 01:52:09 +01:00
|
|
|
assert vm.is_template(), "VM {0} is not a TemplateVM!".format(vm.name)
|
2010-04-05 20:58:57 +02:00
|
|
|
self.default_template_qid = vm.qid
|
|
|
|
|
|
|
|
def get_default_template_vm(self):
|
|
|
|
if self.default_template_qid is None:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return self[self.default_template_qid]
|
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
def set_default_netvm(self, vm):
|
2011-03-06 17:06:45 +01:00
|
|
|
assert vm.is_netvm(), "VM {0} does not provide network!".format(vm.name)
|
2010-04-05 20:58:57 +02:00
|
|
|
self.default_netvm_qid = vm.qid
|
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
def get_default_netvm(self):
|
2010-04-05 20:58:57 +02:00
|
|
|
if self.default_netvm_qid is None:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return self[self.default_netvm_qid]
|
|
|
|
|
2011-06-30 01:07:47 +02:00
|
|
|
def set_default_kernel(self, kernel):
|
|
|
|
assert os.path.exists(qubes_kernels_base_dir + '/' + kernel), "Kerel {0} not installed!".format(kernel)
|
|
|
|
self.default_kernel = kernel
|
|
|
|
|
|
|
|
def get_default_kernel(self):
|
|
|
|
return self.default_kernel
|
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
def set_default_fw_netvm(self, vm):
|
2011-03-06 17:06:45 +01:00
|
|
|
assert vm.is_netvm(), "VM {0} does not provide network!".format(vm.name)
|
2011-02-09 21:21:14 +01:00
|
|
|
self.default_fw_netvm_qid = vm.qid
|
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
def get_default_fw_netvm(self):
|
2011-02-09 21:21:14 +01:00
|
|
|
if self.default_fw_netvm_qid is None:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return self[self.default_fw_netvm_qid]
|
|
|
|
|
2011-06-22 00:44:48 +02:00
|
|
|
def set_updatevm_vm(self, vm):
|
|
|
|
self.updatevm_qid = vm.qid
|
|
|
|
|
|
|
|
def get_updatevm_vm(self):
|
|
|
|
if self.updatevm_qid is None:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return self[self.updatevm_qid]
|
|
|
|
|
2011-10-01 02:52:56 +02:00
|
|
|
def set_clockvm_vm(self, vm):
|
|
|
|
self.clockvm_qid = vm.qid
|
|
|
|
|
|
|
|
def get_clockvm_vm(self):
|
|
|
|
if self.clockvm_qid is None:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return self[self.clockvm_qid]
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def get_vm_by_name(self, name):
|
|
|
|
for vm in self.values():
|
|
|
|
if (vm.name == name):
|
|
|
|
return vm
|
|
|
|
return None
|
|
|
|
|
|
|
|
def get_qid_by_name(self, name):
|
|
|
|
vm = self.get_vm_by_name(name)
|
|
|
|
return vm.qid if vm is not None else None
|
|
|
|
|
|
|
|
def get_vms_based_on(self, template_qid):
|
|
|
|
vms = set([vm for vm in self.values()
|
2011-04-05 14:33:51 +02:00
|
|
|
if (vm.template_vm and vm.template_vm.qid == template_qid)])
|
2010-04-05 20:58:57 +02:00
|
|
|
return vms
|
2011-03-02 15:00:19 +01:00
|
|
|
|
2011-03-31 01:58:45 +02:00
|
|
|
def get_vms_connected_to(self, netvm_qid):
|
|
|
|
new_vms = [ netvm_qid ]
|
|
|
|
dependend_vms_qid = []
|
|
|
|
|
|
|
|
# Dependency resolving only makes sense on NetVM (or derivative)
|
|
|
|
if not self[netvm_qid].is_netvm():
|
|
|
|
return set([])
|
|
|
|
|
|
|
|
while len(new_vms) > 0:
|
|
|
|
cur_vm = new_vms.pop()
|
2011-04-07 10:42:24 +02:00
|
|
|
for vm in self[cur_vm].connected_vms.values():
|
2011-04-04 19:08:40 +02:00
|
|
|
if vm.qid not in dependend_vms_qid:
|
2011-03-31 01:58:45 +02:00
|
|
|
dependend_vms_qid.append(vm.qid)
|
|
|
|
if vm.is_netvm():
|
|
|
|
new_vms.append(vm.qid)
|
|
|
|
|
|
|
|
vms = [vm for vm in self.values() if vm.qid in dependend_vms_qid]
|
|
|
|
return vms
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def verify_new_vm(self, new_vm):
|
|
|
|
|
|
|
|
# Verify that qid is unique
|
|
|
|
for vm in self.values():
|
|
|
|
if vm.qid == new_vm.qid:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "ERROR: The qid={0} is already used by VM '{1}'!".\
|
2010-04-05 20:58:57 +02:00
|
|
|
format(vm.qid, vm.name)
|
|
|
|
return False
|
|
|
|
|
|
|
|
# Verify that name is unique
|
|
|
|
for vm in self.values():
|
|
|
|
if vm.name == new_vm.name:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "ERROR: The name={0} is already used by other VM with qid='{1}'!".\
|
2010-04-05 20:58:57 +02:00
|
|
|
format(vm.name, vm.qid)
|
|
|
|
return False
|
|
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
def get_new_unused_qid(self):
|
|
|
|
used_ids = set([vm.qid for vm in self.values()])
|
|
|
|
for id in range (1, qubes_max_qid):
|
|
|
|
if id not in used_ids:
|
|
|
|
return id
|
|
|
|
raise LookupError ("Cannot find unused qid!")
|
|
|
|
|
|
|
|
def get_new_unused_netid(self):
|
|
|
|
used_ids = set([vm.netid for vm in self.values() if vm.is_netvm()])
|
|
|
|
for id in range (1, qubes_max_netid):
|
|
|
|
if id not in used_ids:
|
|
|
|
return id
|
|
|
|
raise LookupError ("Cannot find unused netid!")
|
|
|
|
|
|
|
|
|
|
|
|
def check_if_storage_exists(self):
|
|
|
|
try:
|
2010-06-26 15:00:19 +02:00
|
|
|
f = open (self.qubes_store_filename, 'r')
|
2010-04-05 20:58:57 +02:00
|
|
|
except IOError:
|
|
|
|
return False
|
|
|
|
f.close()
|
|
|
|
return True
|
|
|
|
|
|
|
|
def create_empty_storage(self):
|
2010-06-26 15:00:19 +02:00
|
|
|
self.qubes_store_file = open (self.qubes_store_filename, 'w')
|
2010-04-05 20:58:57 +02:00
|
|
|
self.clear()
|
|
|
|
self.save()
|
|
|
|
|
|
|
|
def lock_db_for_reading(self):
|
2010-06-26 15:00:19 +02:00
|
|
|
self.qubes_store_file = open (self.qubes_store_filename, 'r')
|
2010-04-05 20:58:57 +02:00
|
|
|
fcntl.lockf (self.qubes_store_file, fcntl.LOCK_SH)
|
|
|
|
|
|
|
|
def lock_db_for_writing(self):
|
2010-06-26 15:00:19 +02:00
|
|
|
self.qubes_store_file = open (self.qubes_store_filename, 'r+')
|
2010-04-05 20:58:57 +02:00
|
|
|
fcntl.lockf (self.qubes_store_file, fcntl.LOCK_EX)
|
|
|
|
|
|
|
|
def unlock_db(self):
|
|
|
|
fcntl.lockf (self.qubes_store_file, fcntl.LOCK_UN)
|
|
|
|
self.qubes_store_file.close()
|
|
|
|
|
|
|
|
def save(self):
|
|
|
|
root = xml.etree.ElementTree.Element(
|
|
|
|
"QubesVmCollection",
|
|
|
|
|
|
|
|
default_template=str(self.default_template_qid) \
|
|
|
|
if self.default_template_qid is not None else "None",
|
|
|
|
|
|
|
|
default_netvm=str(self.default_netvm_qid) \
|
2011-03-11 02:12:23 +01:00
|
|
|
if self.default_netvm_qid is not None else "None",
|
2011-03-11 02:09:32 +01:00
|
|
|
|
|
|
|
default_fw_netvm=str(self.default_fw_netvm_qid) \
|
2011-06-22 00:44:48 +02:00
|
|
|
if self.default_fw_netvm_qid is not None else "None",
|
|
|
|
|
|
|
|
updatevm=str(self.updatevm_qid) \
|
2011-06-30 01:07:47 +02:00
|
|
|
if self.updatevm_qid is not None else "None",
|
|
|
|
|
2011-10-01 02:52:56 +02:00
|
|
|
clockvm=str(self.clockvm_qid) \
|
|
|
|
if self.clockvm_qid is not None else "None",
|
|
|
|
|
2011-06-30 01:07:47 +02:00
|
|
|
default_kernel=str(self.default_kernel) \
|
|
|
|
if self.default_kernel is not None else "None",
|
2010-04-05 20:58:57 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
for vm in self.values():
|
|
|
|
element = vm.create_xml_element()
|
|
|
|
if element is not None:
|
|
|
|
root.append(element)
|
|
|
|
tree = xml.etree.ElementTree.ElementTree(root)
|
|
|
|
|
|
|
|
try:
|
2011-03-02 15:00:19 +01:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
# We need to manually truncate the file, as we open the
|
|
|
|
# file as "r+" in the lock_db_for_writing() function
|
|
|
|
self.qubes_store_file.seek (0, os.SEEK_SET)
|
|
|
|
self.qubes_store_file.truncate()
|
|
|
|
tree.write(self.qubes_store_file, "UTF-8")
|
|
|
|
except EnvironmentError as err:
|
2011-02-21 18:13:27 +01:00
|
|
|
print("{0}: export error: {1}".format(
|
2010-04-05 20:58:57 +02:00
|
|
|
os.path.basename(sys.argv[0]), err))
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2011-03-17 01:40:15 +01:00
|
|
|
def parse_xml_element(self, element):
|
|
|
|
kwargs = {}
|
|
|
|
common_attr_list = ("qid", "name", "dir_path", "conf_file",
|
|
|
|
"private_img", "root_img", "template_qid",
|
2011-04-04 00:08:24 +02:00
|
|
|
"installed_by_rpm", "updateable", "internal",
|
2011-04-29 01:38:43 +02:00
|
|
|
"uses_default_netvm", "label", "memory", "vcpus", "pcidevs",
|
2011-10-01 02:49:25 +02:00
|
|
|
"maxmem", "kernel", "uses_default_kernel", "kernelopts", "uses_default_kernelopts",
|
2012-03-02 16:07:31 +01:00
|
|
|
"mac", "services" )
|
2011-03-17 01:40:15 +01:00
|
|
|
|
|
|
|
for attribute in common_attr_list:
|
|
|
|
kwargs[attribute] = element.get(attribute)
|
2011-10-01 02:56:26 +02:00
|
|
|
if kwargs[attribute] is None:
|
|
|
|
kwargs.pop(attribute)
|
2011-03-17 01:40:15 +01:00
|
|
|
|
|
|
|
kwargs["qid"] = int(kwargs["qid"])
|
2011-10-01 02:56:26 +02:00
|
|
|
if "updateable" in kwargs:
|
2011-03-17 01:40:15 +01:00
|
|
|
kwargs["updateable"] = True if kwargs["updateable"] == "True" else False
|
|
|
|
|
|
|
|
if "installed_by_rpm" in kwargs:
|
|
|
|
kwargs["installed_by_rpm"] = True if kwargs["installed_by_rpm"] == "True" else False
|
|
|
|
|
2011-04-03 17:47:20 +02:00
|
|
|
if "internal" in kwargs:
|
|
|
|
kwargs["internal"] = True if kwargs["internal"] == "True" else False
|
|
|
|
|
2011-03-17 01:40:15 +01:00
|
|
|
if "template_qid" in kwargs:
|
|
|
|
if kwargs["template_qid"] == "none" or kwargs["template_qid"] is None:
|
|
|
|
kwargs.pop("template_qid")
|
|
|
|
else:
|
|
|
|
kwargs["template_qid"] = int(kwargs["template_qid"])
|
|
|
|
template_vm = self[kwargs.pop("template_qid")]
|
|
|
|
if template_vm is None:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "ERROR: VM '{0}' uses unkown template qid='{1}'!".\
|
2011-03-17 01:40:15 +01:00
|
|
|
format(kwargs["name"], kwargs["template_qid"])
|
2011-10-07 21:44:46 +02:00
|
|
|
else:
|
|
|
|
kwargs["template_vm"] = template_vm
|
2011-03-17 01:40:15 +01:00
|
|
|
|
2011-10-01 02:56:26 +02:00
|
|
|
if "label" in kwargs:
|
2011-03-17 01:40:15 +01:00
|
|
|
if kwargs["label"] not in QubesVmLabels:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "ERROR: incorrect label for VM '{0}'".format(kwargs["name"])
|
2011-03-17 01:40:15 +01:00
|
|
|
kwargs.pop ("label")
|
|
|
|
else:
|
|
|
|
kwargs["label"] = QubesVmLabels[kwargs["label"]]
|
|
|
|
|
2011-06-30 01:07:47 +02:00
|
|
|
if "kernel" in kwargs and kwargs["kernel"] == "None":
|
|
|
|
kwargs["kernel"] = None
|
|
|
|
if "uses_default_kernel" in kwargs:
|
|
|
|
kwargs["uses_default_kernel"] = True if kwargs["uses_default_kernel"] == "True" else False
|
|
|
|
else:
|
|
|
|
# For backward compatibility
|
|
|
|
kwargs["uses_default_kernel"] = False
|
|
|
|
if kwargs["uses_default_kernel"]:
|
|
|
|
kwargs["kernel"] = self.get_default_kernel()
|
|
|
|
else:
|
|
|
|
if "kernel" in kwargs and kwargs["kernel"]=="None":
|
|
|
|
kwargs["kernel"]=None
|
|
|
|
# for other cases - generic assigment is ok
|
|
|
|
|
2011-08-31 20:34:56 +02:00
|
|
|
if "uses_default_kernelopts" in kwargs:
|
2011-09-09 14:24:17 +02:00
|
|
|
kwargs["uses_default_kernelopts"] = False if kwargs["uses_default_kernelopts"] == "False" else True
|
|
|
|
|
2012-02-27 03:30:35 +01:00
|
|
|
if "kernelopts" in kwargs and kwargs["kernelopts"] == "None":
|
2011-09-09 14:24:17 +02:00
|
|
|
kwargs.pop("kernelopts")
|
2012-02-27 03:30:35 +01:00
|
|
|
if "kernelopts" not in kwargs:
|
2011-09-09 14:24:17 +02:00
|
|
|
kwargs["uses_default_kernelopts"] = True
|
2011-08-31 20:34:56 +02:00
|
|
|
|
2011-03-17 01:40:15 +01:00
|
|
|
return kwargs
|
|
|
|
|
|
|
|
def set_netvm_dependency(self, element):
|
|
|
|
kwargs = {}
|
|
|
|
attr_list = ("qid", "uses_default_netvm", "netvm_qid")
|
|
|
|
|
|
|
|
for attribute in attr_list:
|
|
|
|
kwargs[attribute] = element.get(attribute)
|
|
|
|
|
|
|
|
vm = self[int(kwargs["qid"])]
|
|
|
|
|
|
|
|
if "uses_default_netvm" not in kwargs:
|
|
|
|
vm.uses_default_netvm = True
|
|
|
|
else:
|
|
|
|
vm.uses_default_netvm = True if kwargs["uses_default_netvm"] == "True" else False
|
|
|
|
if vm.uses_default_netvm is True:
|
2012-03-04 21:59:02 +01:00
|
|
|
netvm = self.get_default_netvm()
|
2011-03-17 01:40:15 +01:00
|
|
|
kwargs.pop("netvm_qid")
|
|
|
|
else:
|
|
|
|
if kwargs["netvm_qid"] == "none" or kwargs["netvm_qid"] is None:
|
2012-03-04 21:59:02 +01:00
|
|
|
netvm = None
|
2011-03-17 01:40:15 +01:00
|
|
|
kwargs.pop("netvm_qid")
|
|
|
|
else:
|
|
|
|
netvm_qid = int(kwargs.pop("netvm_qid"))
|
|
|
|
if netvm_qid not in self:
|
2012-03-04 21:59:02 +01:00
|
|
|
netvm = None
|
2011-03-17 01:40:15 +01:00
|
|
|
else:
|
2012-03-04 21:59:02 +01:00
|
|
|
netvm = self[netvm_qid]
|
2011-03-17 01:40:15 +01:00
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
vm.netvm = netvm
|
|
|
|
if netvm:
|
|
|
|
netvm.connected_vms[vm.qid] = vm
|
2011-03-17 01:40:15 +01:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
def load(self):
|
|
|
|
self.clear()
|
|
|
|
|
|
|
|
dom0vm = QubesDom0NetVm ()
|
|
|
|
self[dom0vm.qid] = dom0vm
|
|
|
|
self.default_netvm_qid = 0
|
|
|
|
|
|
|
|
global dom0_vm
|
|
|
|
dom0_vm = dom0vm
|
|
|
|
|
|
|
|
try:
|
|
|
|
tree = xml.etree.ElementTree.parse(self.qubes_store_file)
|
|
|
|
except (EnvironmentError,
|
|
|
|
xml.parsers.expat.ExpatError) as err:
|
|
|
|
print("{0}: import error: {1}".format(
|
|
|
|
os.path.basename(sys.argv[0]), err))
|
|
|
|
return False
|
|
|
|
|
|
|
|
element = tree.getroot()
|
2011-03-02 15:00:19 +01:00
|
|
|
default_template = element.get("default_template")
|
2010-04-05 20:58:57 +02:00
|
|
|
self.default_template_qid = int(default_template) \
|
|
|
|
if default_template != "None" else None
|
|
|
|
|
2011-03-02 15:00:19 +01:00
|
|
|
default_netvm = element.get("default_netvm")
|
2010-04-05 20:58:57 +02:00
|
|
|
if default_netvm is not None:
|
|
|
|
self.default_netvm_qid = int(default_netvm) \
|
|
|
|
if default_netvm != "None" else None
|
2011-03-11 02:09:32 +01:00
|
|
|
#assert self.default_netvm_qid is not None
|
|
|
|
|
2011-10-01 02:55:22 +02:00
|
|
|
default_fw_netvm = element.get("default_fw_netvm")
|
2011-03-11 02:09:32 +01:00
|
|
|
if default_fw_netvm is not None:
|
|
|
|
self.default_fw_netvm_qid = int(default_fw_netvm) \
|
|
|
|
if default_fw_netvm != "None" else None
|
2010-04-05 20:58:57 +02:00
|
|
|
#assert self.default_netvm_qid is not None
|
|
|
|
|
2011-06-22 00:44:48 +02:00
|
|
|
updatevm = element.get("updatevm")
|
|
|
|
if updatevm is not None:
|
|
|
|
self.updatevm_qid = int(updatevm) \
|
|
|
|
if updatevm != "None" else None
|
|
|
|
#assert self.default_netvm_qid is not None
|
|
|
|
|
2011-10-01 02:52:56 +02:00
|
|
|
clockvm = element.get("clockvm")
|
|
|
|
if clockvm is not None:
|
|
|
|
self.clockvm_qid = int(clockvm) \
|
|
|
|
if clockvm != "None" else None
|
|
|
|
|
2011-06-30 01:07:47 +02:00
|
|
|
self.default_kernel = element.get("default_kernel")
|
2011-06-22 00:44:48 +02:00
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
# Then, read in the TemplateVMs, because a reference to template VM
|
|
|
|
# is needed to create each AppVM
|
|
|
|
for element in tree.findall("QubesTemplateVm"):
|
|
|
|
try:
|
|
|
|
|
2011-03-17 01:40:15 +01:00
|
|
|
kwargs = self.parse_xml_element(element)
|
2011-03-06 17:06:45 +01:00
|
|
|
|
|
|
|
vm = QubesTemplateVm(**kwargs)
|
|
|
|
|
|
|
|
self[vm.qid] = vm
|
|
|
|
except (ValueError, LookupError) as err:
|
|
|
|
print("{0}: import error (QubesTemplateVm): {1}".format(
|
|
|
|
os.path.basename(sys.argv[0]), err))
|
|
|
|
return False
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
# Read in the NetVMs first, because a reference to NetVM
|
|
|
|
# is needed to create all other VMs
|
|
|
|
for element in tree.findall("QubesNetVm"):
|
|
|
|
try:
|
2011-03-17 01:40:15 +01:00
|
|
|
kwargs = self.parse_xml_element(element)
|
|
|
|
# Add NetVM specific fields
|
|
|
|
attr_list = ("netid",)
|
2010-04-05 20:58:57 +02:00
|
|
|
|
|
|
|
for attribute in attr_list:
|
|
|
|
kwargs[attribute] = element.get(attribute)
|
|
|
|
|
|
|
|
kwargs["netid"] = int(kwargs["netid"])
|
|
|
|
|
|
|
|
vm = QubesNetVm(**kwargs)
|
|
|
|
self[vm.qid] = vm
|
2011-03-02 15:00:19 +01:00
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
except (ValueError, LookupError) as err:
|
|
|
|
print("{0}: import error (QubesNetVM) {1}".format(
|
|
|
|
os.path.basename(sys.argv[0]), err))
|
|
|
|
return False
|
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
# Next read in the ProxyVMs, because they may be referenced
|
2011-02-09 21:21:14 +01:00
|
|
|
# by other VMs
|
2011-03-06 17:06:45 +01:00
|
|
|
for element in tree.findall("QubesProxyVm"):
|
2011-02-09 21:21:14 +01:00
|
|
|
try:
|
2011-03-17 01:40:15 +01:00
|
|
|
kwargs = self.parse_xml_element(element)
|
|
|
|
# Add ProxyVM specific fields
|
|
|
|
attr_list = ("netid",)
|
2011-02-09 21:21:14 +01:00
|
|
|
|
|
|
|
for attribute in attr_list:
|
|
|
|
kwargs[attribute] = element.get(attribute)
|
|
|
|
|
|
|
|
kwargs["netid"] = int(kwargs["netid"])
|
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
vm = QubesProxyVm(**kwargs)
|
2011-02-09 21:21:14 +01:00
|
|
|
self[vm.qid] = vm
|
2011-03-02 15:00:19 +01:00
|
|
|
|
2011-02-09 21:21:14 +01:00
|
|
|
except (ValueError, LookupError) as err:
|
2011-03-06 17:06:45 +01:00
|
|
|
print("{0}: import error (QubesProxyVM) {1}".format(
|
2011-02-09 21:21:14 +01:00
|
|
|
os.path.basename(sys.argv[0]), err))
|
|
|
|
return False
|
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
# After importing all NetVMs and ProxyVMs, set netvm references
|
|
|
|
# 1. For TemplateVMs
|
2010-04-05 20:58:57 +02:00
|
|
|
for element in tree.findall("QubesTemplateVm"):
|
|
|
|
try:
|
2011-03-17 01:40:15 +01:00
|
|
|
self.set_netvm_dependency(element)
|
2010-04-05 20:58:57 +02:00
|
|
|
except (ValueError, LookupError) as err:
|
|
|
|
print("{0}: import error (QubesTemplateVm): {1}".format(
|
|
|
|
os.path.basename(sys.argv[0]), err))
|
|
|
|
return False
|
|
|
|
|
2011-03-06 17:06:45 +01:00
|
|
|
# 2. For PoxyVMs
|
|
|
|
for element in tree.findall("QubesProxyVm"):
|
|
|
|
try:
|
2011-03-17 01:40:15 +01:00
|
|
|
self.set_netvm_dependency(element)
|
2011-03-06 17:06:45 +01:00
|
|
|
except (ValueError, LookupError) as err:
|
|
|
|
print("{0}: import error (QubesProxyVM) {1}".format(
|
|
|
|
os.path.basename(sys.argv[0]), err))
|
|
|
|
return False
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
# Finally, read in the AppVMs
|
|
|
|
for element in tree.findall("QubesAppVm"):
|
|
|
|
try:
|
2011-03-17 01:40:15 +01:00
|
|
|
kwargs = self.parse_xml_element(element)
|
2010-04-05 20:58:57 +02:00
|
|
|
vm = QubesAppVm(**kwargs)
|
|
|
|
|
|
|
|
self[vm.qid] = vm
|
2011-03-17 01:40:15 +01:00
|
|
|
|
|
|
|
self.set_netvm_dependency(element)
|
2010-04-05 20:58:57 +02:00
|
|
|
except (ValueError, LookupError) as err:
|
|
|
|
print("{0}: import error (QubesAppVm): {1}".format(
|
|
|
|
os.path.basename(sys.argv[0]), err))
|
|
|
|
return False
|
|
|
|
|
2012-02-24 04:23:27 +01:00
|
|
|
# And HVMs
|
|
|
|
for element in tree.findall("QubesHVm"):
|
|
|
|
try:
|
|
|
|
kwargs = self.parse_xml_element(element)
|
|
|
|
vm = QubesHVm(**kwargs)
|
|
|
|
|
|
|
|
self[vm.qid] = vm
|
|
|
|
|
|
|
|
self.set_netvm_dependency(element)
|
|
|
|
except (ValueError, LookupError) as err:
|
|
|
|
print("{0}: import error (QubesHVm): {1}".format(
|
|
|
|
os.path.basename(sys.argv[0]), err))
|
|
|
|
return False
|
|
|
|
|
2010-09-21 15:59:22 +02:00
|
|
|
# Really finally, read in the DisposableVMs
|
|
|
|
for element in tree.findall("QubesDisposableVm"):
|
|
|
|
try:
|
|
|
|
kwargs = {}
|
|
|
|
attr_list = ("qid", "name",
|
|
|
|
"template_qid",
|
2011-10-31 01:28:46 +01:00
|
|
|
"label", "dispid", "firewall_conf" )
|
2010-09-21 15:59:22 +02:00
|
|
|
|
|
|
|
for attribute in attr_list:
|
|
|
|
kwargs[attribute] = element.get(attribute)
|
|
|
|
|
|
|
|
kwargs["qid"] = int(kwargs["qid"])
|
2011-07-09 17:52:47 +02:00
|
|
|
kwargs["dispid"] = int(kwargs["dispid"])
|
2010-09-21 15:59:22 +02:00
|
|
|
kwargs["template_qid"] = int(kwargs["template_qid"])
|
|
|
|
|
|
|
|
template_vm = self[kwargs.pop("template_qid")]
|
|
|
|
if template_vm is None:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "ERROR: DisposableVM '{0}' uses unkown template qid='{1}'!".\
|
2010-09-21 15:59:22 +02:00
|
|
|
format(kwargs["name"], kwargs["template_qid"])
|
2011-10-07 21:44:46 +02:00
|
|
|
else:
|
|
|
|
kwargs["template_vm"] = template_vm
|
2010-09-21 15:59:22 +02:00
|
|
|
|
2012-03-04 21:59:02 +01:00
|
|
|
kwargs["netvm"] = self.get_default_netvm()
|
2011-03-02 15:00:19 +01:00
|
|
|
|
2010-09-21 15:59:22 +02:00
|
|
|
if kwargs["label"] is not None:
|
|
|
|
if kwargs["label"] not in QubesVmLabels:
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "ERROR: incorrect label for VM '{0}'".format(kwargs["name"])
|
2010-09-21 15:59:22 +02:00
|
|
|
kwargs.pop ("label")
|
|
|
|
else:
|
|
|
|
kwargs["label"] = QubesVmLabels[kwargs["label"]]
|
|
|
|
|
|
|
|
vm = QubesDisposableVm(**kwargs)
|
|
|
|
|
|
|
|
self[vm.qid] = vm
|
|
|
|
except (ValueError, LookupError) as err:
|
|
|
|
print("{0}: import error (DisposableAppVm): {1}".format(
|
|
|
|
os.path.basename(sys.argv[0]), err))
|
|
|
|
return False
|
|
|
|
|
2011-10-01 02:52:56 +02:00
|
|
|
# if there was no clockvm entry in qubes.xml, try to determine default:
|
|
|
|
# root of default NetVM chain
|
|
|
|
if clockvm is None:
|
|
|
|
if self.default_netvm_qid is not None:
|
|
|
|
clockvm = self[self.default_netvm_qid]
|
|
|
|
# Find root of netvm chain
|
2012-03-04 21:59:02 +01:00
|
|
|
while clockvm.netvm is not None:
|
|
|
|
clockvm = clockvm.netvm
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-10-01 02:52:56 +02:00
|
|
|
self.clockvm_qid = clockvm.qid
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2012-02-01 17:51:04 +01:00
|
|
|
# Disable ntpd in ClockVM - to not conflict with ntpdate (both are using 123/udp port)
|
2011-10-01 02:52:56 +02:00
|
|
|
if self.clockvm_qid is not None:
|
2012-02-01 17:51:04 +01:00
|
|
|
self[self.clockvm_qid].services['ntpd'] = False
|
2011-10-01 02:52:56 +02:00
|
|
|
return True
|
2010-04-05 20:58:57 +02:00
|
|
|
|
2011-12-23 17:08:14 +01:00
|
|
|
def pop(self, qid):
|
|
|
|
if self.default_netvm_qid == qid:
|
|
|
|
self.default_netvm_qid = None
|
|
|
|
if self.default_fw_netvm_qid == qid:
|
|
|
|
self.default_fw_netvm_qid = None
|
|
|
|
if self.clockvm_qid == qid:
|
|
|
|
self.clockvm_qid = None
|
|
|
|
if self.updatevm_qid == qid:
|
|
|
|
self.updatevm_qid = None
|
|
|
|
if self.default_template_qid == qid:
|
|
|
|
self.default_template_qid = None
|
|
|
|
|
|
|
|
return super(QubesVmCollection, self).pop(qid)
|
|
|
|
|
2010-04-05 20:58:57 +02:00
|
|
|
class QubesDaemonPidfile(object):
|
|
|
|
def __init__(self, name):
|
|
|
|
self.name = name
|
|
|
|
self.path = "/var/run/qubes/" + name + ".pid"
|
|
|
|
|
|
|
|
def create_pidfile(self):
|
|
|
|
f = open (self.path, 'w')
|
|
|
|
f.write(str(os.getpid()))
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
def pidfile_exists(self):
|
|
|
|
return os.path.exists(self.path)
|
|
|
|
|
|
|
|
def read_pid(self):
|
|
|
|
f = open (self.path)
|
|
|
|
pid = f.read ().strip()
|
|
|
|
f.close()
|
|
|
|
return int(pid)
|
|
|
|
|
|
|
|
def pidfile_is_stale(self):
|
|
|
|
if not self.pidfile_exists():
|
|
|
|
return False
|
|
|
|
|
|
|
|
# check if the pid file is valid...
|
|
|
|
proc_path = "/proc/" + str(self.read_pid()) + "/cmdline"
|
|
|
|
if not os.path.exists (proc_path):
|
2011-10-07 21:40:29 +02:00
|
|
|
print >> sys.stderr, "Path {0} doesn't exist, assuming stale pidfile.".format(proc_path)
|
2010-04-05 20:58:57 +02:00
|
|
|
return True
|
|
|
|
|
|
|
|
f = open (proc_path)
|
|
|
|
cmdline = f.read ()
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
# The following doesn't work with python -- one would have to get argv[1] and compare it with self.name...
|
|
|
|
# if not cmdline.strip().endswith(self.name):
|
2011-10-07 21:40:29 +02:00
|
|
|
# print >> sys.stderr, "{0} = {1} doesn't seem to point to our process ({2}), assuming stale pidile.".format(proc_path, cmdline, self.name)
|
2010-04-05 20:58:57 +02:00
|
|
|
# return True
|
|
|
|
|
|
|
|
return False # It's a good pidfile
|
|
|
|
|
|
|
|
def remove_pidfile(self):
|
|
|
|
os.remove (self.path)
|
|
|
|
|
|
|
|
def __enter__ (self):
|
|
|
|
# assumes the pidfile doesn't exist -- you should ensure it before opening the context
|
|
|
|
self.create_pidfile()
|
|
|
|
def __exit__ (self):
|
|
|
|
self.remove_pidfile()
|
|
|
|
|
|
|
|
|
2011-02-27 00:06:46 +01:00
|
|
|
# vim:sw=4:et:
|