Merge branch 'master' of git.qubes-os.org:/var/lib/qubes/git/marmarek/core
This commit is contained in:
commit
3f31a5f3a7
@ -178,6 +178,11 @@ class QubesVm(object):
|
||||
root_img = None,
|
||||
private_img = None,
|
||||
memory = default_memory,
|
||||
template_vm = None,
|
||||
firewall_conf = None,
|
||||
volatile_img = None,
|
||||
pcidevs = None,
|
||||
internal = False,
|
||||
vcpus = None):
|
||||
|
||||
|
||||
@ -200,9 +205,6 @@ class QubesVm(object):
|
||||
self.uses_default_netvm = uses_default_netvm
|
||||
self.netvm_vm = netvm_vm
|
||||
|
||||
# Create template_vm property - used in AppVM, NetVM, ProxyVM
|
||||
self.template_vm = None
|
||||
|
||||
# We use it in remove from disk to avoid removing rpm files (for templates)
|
||||
self.installed_by_rpm = installed_by_rpm
|
||||
|
||||
@ -221,6 +223,11 @@ class QubesVm(object):
|
||||
self.private_img = dir_path + "/" + (
|
||||
private_img if private_img is not None else default_private_img)
|
||||
|
||||
if firewall_conf is None:
|
||||
self.firewall_conf = dir_path + "/" + default_firewall_conf_file
|
||||
else:
|
||||
self.firewall_conf = firewall_conf
|
||||
|
||||
self.updateable = updateable
|
||||
self.label = label if label is not None else QubesVmLabels["red"]
|
||||
if self.dir_path is not None:
|
||||
@ -229,10 +236,27 @@ class QubesVm(object):
|
||||
self.icon_path = None
|
||||
|
||||
# PCI devices - used only by NetVM
|
||||
self.pcidevs = ""
|
||||
if pcidevs is None or pcidevs == "none":
|
||||
self.pcidevs = ""
|
||||
else:
|
||||
self.pcidevs = pcidevs
|
||||
|
||||
self.memory = memory
|
||||
|
||||
self.template_vm = template_vm
|
||||
if template_vm is not None:
|
||||
if updateable:
|
||||
print "ERROR: Template based VM cannot be updateable!"
|
||||
return False
|
||||
if not template_vm.is_template():
|
||||
print "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\
|
||||
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!"
|
||||
|
||||
# By default allow use all VCPUs
|
||||
if vcpus is None:
|
||||
qubes_host = QubesHost()
|
||||
@ -240,6 +264,9 @@ class QubesVm(object):
|
||||
else:
|
||||
self.vcpus = vcpus
|
||||
|
||||
# Internal VM (not shown in qubes-manager, doesn't create appmenus entries
|
||||
self.internal = internal
|
||||
|
||||
if not dry_run and xend_session.session is not None:
|
||||
self.refresh_xend_session()
|
||||
|
||||
@ -287,14 +314,17 @@ class QubesVm(object):
|
||||
else:
|
||||
return False
|
||||
|
||||
def set_updateable(self):
|
||||
if self.is_updateable():
|
||||
return
|
||||
|
||||
raise QubesException ("Change 'updateable' flag is not supported. Please use qvm-create.")
|
||||
|
||||
def set_nonupdateable(self):
|
||||
if not self.is_updateable():
|
||||
return
|
||||
|
||||
assert not self.is_running()
|
||||
# We can always downgrade a VM to non-updateable...
|
||||
self.updateable = False
|
||||
raise QubesException ("Change 'updateable' flag is not supported. Please use qvm-create.")
|
||||
|
||||
def is_template(self):
|
||||
return isinstance(self, QubesTemplateVm)
|
||||
@ -455,6 +485,30 @@ class QubesVm(object):
|
||||
else:
|
||||
return False
|
||||
|
||||
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)
|
||||
|
||||
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 :(
|
||||
p = subprocess.Popen (["xenstore-read",
|
||||
"/local/domain/0/backend/vbd/{0}/51712/node".format(self.get_xid())],
|
||||
stdout=subprocess.PIPE)
|
||||
used_dmdev = p.communicate()[0].strip()
|
||||
|
||||
return used_dmdev != current_dmdev
|
||||
|
||||
def get_disk_usage(self, file_or_dir):
|
||||
if not os.path.exists(file_or_dir):
|
||||
return 0
|
||||
@ -535,6 +589,220 @@ class QubesVm(object):
|
||||
"/local/domain/{0}/qubes_secondary_dns".format(xid),
|
||||
self.netvm_vm.secondary_dns])
|
||||
|
||||
def create_config_file(self):
|
||||
assert self.template_vm is not None
|
||||
|
||||
conf_template = None
|
||||
if self.type == "NetVM":
|
||||
conf_template = open (self.template_vm.netvms_conf_file, "r")
|
||||
elif self.updateable:
|
||||
conf_template = open (self.template_vm.standalonevms_conf_file, "r")
|
||||
else:
|
||||
conf_template = open (self.template_vm.appvms_conf_file, "r")
|
||||
if os.path.isfile(self.conf_file):
|
||||
shutil.copy(self.conf_file, self.conf_file + ".backup")
|
||||
conf_appvm = open(self.conf_file, "w")
|
||||
rx_vmname = re.compile (r"%VMNAME%")
|
||||
rx_vmdir = re.compile (r"%VMDIR%")
|
||||
rx_template = re.compile (r"%TEMPLATEDIR%")
|
||||
rx_pcidevs = re.compile (r"%PCIDEVS%")
|
||||
rx_mem = re.compile (r"%MEM%")
|
||||
rx_vcpus = re.compile (r"%VCPUS%")
|
||||
|
||||
for line in conf_template:
|
||||
line = rx_vmname.sub (self.name, line)
|
||||
line = rx_vmdir.sub (self.dir_path, line)
|
||||
line = rx_template.sub (self.template_vm.dir_path, line)
|
||||
line = rx_pcidevs.sub (self.pcidevs, line)
|
||||
line = rx_mem.sub (str(self.memory), line)
|
||||
line = rx_vcpus.sub (str(self.vcpus), line)
|
||||
conf_appvm.write(line)
|
||||
|
||||
conf_template.close()
|
||||
conf_appvm.close()
|
||||
|
||||
def create_on_disk(self, verbose):
|
||||
assert self.template_vm is not None
|
||||
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
if verbose:
|
||||
print "--> Creating directory: {0}".format(self.dir_path)
|
||||
os.mkdir (self.dir_path)
|
||||
|
||||
if verbose:
|
||||
print "--> Creating the VM config file: {0}".format(self.conf_file)
|
||||
|
||||
self.create_config_file()
|
||||
|
||||
template_priv = self.template_vm.private_img
|
||||
if verbose:
|
||||
print "--> Copying the template's private image: {0}".\
|
||||
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))
|
||||
|
||||
if self.is_updateable():
|
||||
template_root = self.template_vm.root_img
|
||||
if verbose:
|
||||
print "--> Copying the template's root image: {0}".\
|
||||
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))
|
||||
|
||||
kernels_dir = self.dir_path + '/' + default_kernels_subdir
|
||||
if verbose:
|
||||
print "--> Copying the template's kernel dir: {0}".\
|
||||
format(self.template_vm.kernels_dir)
|
||||
shutil.copytree (self.template_vm.kernels_dir, kernels_dir)
|
||||
|
||||
|
||||
# Create volatile.img
|
||||
self.reset_volatile_storage()
|
||||
|
||||
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.conf_file):
|
||||
raise QubesException (
|
||||
"VM config file doesn't exist: {0}".\
|
||||
format(self.conf_file))
|
||||
|
||||
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))
|
||||
return True
|
||||
|
||||
def reset_volatile_storage(self):
|
||||
assert not self.is_running(), "Attempt to clean volatile image of running VM!"
|
||||
|
||||
# Only makes sense on template based VM
|
||||
if self.template_vm is None:
|
||||
return
|
||||
|
||||
print "--> Cleaning volatile image: {0}...".format (self.volatile_img)
|
||||
if dry_run:
|
||||
return
|
||||
if os.path.exists (self.volatile_img):
|
||||
os.remove (self.volatile_img)
|
||||
|
||||
retcode = subprocess.call (["tar", "xf", self.template_vm.clean_volatile_img, "-C", self.dir_path])
|
||||
if retcode != 0:
|
||||
raise IOError ("Error while unpacking {0} to {1}".\
|
||||
format(self.template_vm.clean_volatile_img, self.volatile_img))
|
||||
|
||||
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"]:
|
||||
element = xml.etree.ElementTree.Element(
|
||||
"rule",
|
||||
address=rule["address"],
|
||||
port=str(rule["portBegin"]),
|
||||
)
|
||||
if rule["netmask"] is not None and rule["netmask"] != 32:
|
||||
element.set("netmask", str(rule["netmask"]))
|
||||
if rule["portEnd"] is not None:
|
||||
element.set("toport", str(rule["portEnd"]))
|
||||
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:
|
||||
print "{0}: save error: {1}".format(
|
||||
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 = {}
|
||||
attr_list = ("address", "netmask", "port", "toport")
|
||||
|
||||
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
|
||||
|
||||
rule["portBegin"] = int(rule["port"])
|
||||
|
||||
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
|
||||
|
||||
def get_total_xen_memory(self):
|
||||
hosts = xend_session.session.xenapi.host.get_all()
|
||||
@ -550,6 +818,8 @@ class QubesVm(object):
|
||||
if self.is_running():
|
||||
raise QubesException ("VM is already running!")
|
||||
|
||||
self.reset_volatile_storage()
|
||||
|
||||
if verbose:
|
||||
print "--> Rereading the VM's conf file ({0})...".format(self.conf_file)
|
||||
self.update_xen_storage()
|
||||
@ -665,10 +935,13 @@ class QubesVm(object):
|
||||
attrs["uses_default_netvm"] = str(self.uses_default_netvm)
|
||||
attrs["netvm_qid"] = str(self.netvm_vm.qid) if self.netvm_vm is not None else "none"
|
||||
attrs["installed_by_rpm"] = str(self.installed_by_rpm)
|
||||
attrs["template_qid"] = str(self.template_vm.qid) if self.template_vm and not self.is_updateable() else "none"
|
||||
attrs["updateable"] = str(self.updateable)
|
||||
attrs["label"] = self.label.name
|
||||
attrs["memory"] = str(self.memory)
|
||||
attrs["pcidevs"] = str(self.pcidevs)
|
||||
attrs["vcpus"] = str(self.vcpus)
|
||||
attrs["internal"] = str(self.internal)
|
||||
return attrs
|
||||
|
||||
def create_xml_element(self):
|
||||
@ -948,196 +1221,7 @@ class QubesTemplateVm(QubesVm):
|
||||
attrs["rootcow_img"] = self.rootcow_img
|
||||
return attrs
|
||||
|
||||
class QubesCowVm(QubesVm):
|
||||
"""
|
||||
A class that represent a VM that may be based on some template, i.e. doesn't have own root.img
|
||||
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
|
||||
kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"]
|
||||
|
||||
if "updateable" not in kwargs or kwargs["updateable"] is None:
|
||||
kwargs["updateable"] = False
|
||||
|
||||
if "template_vm" in kwargs:
|
||||
template_vm = kwargs.pop("template_vm")
|
||||
else:
|
||||
template_vm = None
|
||||
|
||||
super(QubesCowVm, self).__init__(**kwargs)
|
||||
qid = kwargs["qid"]
|
||||
dir_path = kwargs["dir_path"]
|
||||
if not self.is_updateable():
|
||||
# Dirty hack for QubesDom0NetVm...
|
||||
if not isinstance(self, QubesDom0NetVm):
|
||||
assert template_vm is not None, "Missing template_vm for template based VM!"
|
||||
if not template_vm.is_template():
|
||||
print "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\
|
||||
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!"
|
||||
|
||||
self.template_vm = template_vm
|
||||
|
||||
def set_updateable(self):
|
||||
if self.is_updateable():
|
||||
return
|
||||
|
||||
raise QubesException ("Change 'updateable' flag is not supported. Please use qvm-create.")
|
||||
|
||||
def set_nonupdateable(self):
|
||||
if self.is_updateable():
|
||||
return
|
||||
|
||||
raise QubesException ("Change 'updateable' flag is not supported. Please use qvm-create.")
|
||||
|
||||
def create_config_file(self):
|
||||
conf_template = None
|
||||
if self.type == "NetVM":
|
||||
conf_template = open (self.template_vm.netvms_conf_file, "r")
|
||||
elif self.updateable:
|
||||
conf_template = open (self.template_vm.standalonevms_conf_file, "r")
|
||||
else:
|
||||
conf_template = open (self.template_vm.appvms_conf_file, "r")
|
||||
if os.path.isfile(self.conf_file):
|
||||
shutil.copy(self.conf_file, self.conf_file + ".backup")
|
||||
conf_appvm = open(self.conf_file, "w")
|
||||
rx_vmname = re.compile (r"%VMNAME%")
|
||||
rx_vmdir = re.compile (r"%VMDIR%")
|
||||
rx_template = re.compile (r"%TEMPLATEDIR%")
|
||||
rx_pcidevs = re.compile (r"%PCIDEVS%")
|
||||
rx_mem = re.compile (r"%MEM%")
|
||||
rx_vcpus = re.compile (r"%VCPUS%")
|
||||
|
||||
for line in conf_template:
|
||||
line = rx_vmname.sub (self.name, line)
|
||||
line = rx_vmdir.sub (self.dir_path, line)
|
||||
line = rx_template.sub (self.template_vm.dir_path, line)
|
||||
line = rx_pcidevs.sub (self.pcidevs, line)
|
||||
line = rx_mem.sub (str(self.memory), line)
|
||||
line = rx_vcpus.sub (str(self.vcpus), line)
|
||||
conf_appvm.write(line)
|
||||
|
||||
conf_template.close()
|
||||
conf_appvm.close()
|
||||
|
||||
def create_on_disk(self, verbose):
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
if verbose:
|
||||
print "--> Creating directory: {0}".format(self.dir_path)
|
||||
os.mkdir (self.dir_path)
|
||||
|
||||
if verbose:
|
||||
print "--> Creating the VM config file: {0}".format(self.conf_file)
|
||||
|
||||
self.create_config_file()
|
||||
|
||||
template_priv = self.template_vm.private_img
|
||||
if verbose:
|
||||
print "--> Copying the template's private image: {0}".\
|
||||
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))
|
||||
|
||||
if self.is_updateable():
|
||||
template_root = self.template_vm.root_img
|
||||
if verbose:
|
||||
print "--> Copying the template's root image: {0}".\
|
||||
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))
|
||||
|
||||
kernels_dir = self.dir_path + '/' + default_kernels_subdir
|
||||
if verbose:
|
||||
print "--> Copying the template's kernel dir: {0}".\
|
||||
format(self.template_vm.kernels_dir)
|
||||
shutil.copytree (self.template_vm.kernels_dir, kernels_dir)
|
||||
|
||||
|
||||
# Create volatile.img
|
||||
self.reset_volatile_storage()
|
||||
|
||||
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.conf_file):
|
||||
raise QubesException (
|
||||
"VM config file doesn't exist: {0}".\
|
||||
format(self.conf_file))
|
||||
|
||||
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))
|
||||
return True
|
||||
|
||||
def start(self, debug_console = False, verbose = False, preparing_dvm = False):
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
if self.is_running():
|
||||
raise QubesException("VM is already running!")
|
||||
|
||||
self.reset_volatile_storage()
|
||||
|
||||
return super(QubesCowVm, self).start(debug_console=debug_console, verbose=verbose, preparing_dvm=preparing_dvm)
|
||||
|
||||
def reset_volatile_storage(self):
|
||||
assert not self.is_running(), "Attempt to clean volatile image of running VM!"
|
||||
|
||||
# Only makes sense on template based VM
|
||||
if not self.template_vm:
|
||||
return
|
||||
|
||||
print "--> Cleaning volatile image: {0}...".format (self.volatile_img)
|
||||
if dry_run:
|
||||
return
|
||||
if os.path.exists (self.volatile_img):
|
||||
os.remove (self.volatile_img)
|
||||
|
||||
retcode = subprocess.call (["tar", "xf", self.template_vm.clean_volatile_img, "-C", self.dir_path])
|
||||
if retcode != 0:
|
||||
raise IOError ("Error while unpacking {0} to {1}".\
|
||||
format(self.template_vm.clean_volatile_img, self.volatile_img))
|
||||
|
||||
def remove_from_disk(self):
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
subprocess.check_call ([qubes_appmenu_remove_cmd, self.name])
|
||||
shutil.rmtree (self.dir_path)
|
||||
|
||||
def get_xml_attrs(self):
|
||||
attrs = super(QubesCowVm, self).get_xml_attrs()
|
||||
attrs["template_qid"] = str(self.template_vm.qid) if not self.is_updateable() else "none"
|
||||
return attrs
|
||||
|
||||
class QubesNetVm(QubesCowVm):
|
||||
class QubesNetVm(QubesVm):
|
||||
"""
|
||||
A class that represents a NetVM. A child of QubesCowVM.
|
||||
"""
|
||||
@ -1306,8 +1390,12 @@ class QubesProxyVm(QubesNetVm):
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vms = [vm for vm in qvm_collection.values() if vm.is_appvm()]
|
||||
vms = [vm for vm in qvm_collection.values() if vm.has_firewall()]
|
||||
for vm in vms:
|
||||
# Process only VMs connected to this ProxyVM
|
||||
if not vm.netvm_vm or vm.netvm_vm.qid != self.qid:
|
||||
continue
|
||||
|
||||
conf = vm.get_firewall_conf()
|
||||
|
||||
xid = vm.get_xid()
|
||||
@ -1447,19 +1535,10 @@ class QubesDisposableVm(QubesVm):
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
template_vm = kwargs.pop("template_vm")
|
||||
template_vm = kwargs["template_vm"]
|
||||
assert template_vm is not None, "Missing template_vm for DisposableVM!"
|
||||
|
||||
super(QubesDisposableVm, self).__init__(dir_path="/nonexistent", **kwargs)
|
||||
qid = kwargs["qid"]
|
||||
|
||||
assert template_vm is not None, "Missing template_vm for DisposableVM!"
|
||||
if not template_vm.is_template():
|
||||
print "ERROR: template_qid={0} doesn't point to a valid TemplateVM".\
|
||||
format(new_vm.template_vm.qid)
|
||||
return False
|
||||
|
||||
self.template_vm = template_vm
|
||||
template_vm.appvms[qid] = self
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
@ -1477,19 +1556,16 @@ class QubesDisposableVm(QubesVm):
|
||||
return True
|
||||
|
||||
|
||||
class QubesAppVm(QubesCowVm):
|
||||
class QubesAppVm(QubesVm):
|
||||
"""
|
||||
A class that represents an AppVM. A child of QubesVm.
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
|
||||
kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"]
|
||||
|
||||
super(QubesAppVm, self).__init__(**kwargs)
|
||||
dir_path = self.dir_path
|
||||
|
||||
if "firewall_conf" not in kwargs or kwargs["firewall_conf"] is None:
|
||||
kwargs["firewall_conf"] = dir_path + "/" + default_firewall_conf_file
|
||||
|
||||
self.firewall_conf = kwargs["firewall_conf"]
|
||||
|
||||
@property
|
||||
def type(self):
|
||||
@ -1505,7 +1581,15 @@ class QubesAppVm(QubesCowVm):
|
||||
print "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path)
|
||||
os.symlink (self.label.icon_path, self.icon_path)
|
||||
|
||||
self.create_appmenus (verbose)
|
||||
if not self.internal:
|
||||
self.create_appmenus (verbose)
|
||||
|
||||
def remove_from_disk(self):
|
||||
if dry_run:
|
||||
return
|
||||
|
||||
subprocess.check_call ([qubes_appmenu_remove_cmd, self.name])
|
||||
super(QubesAppVm, self).remove_from_disk()
|
||||
|
||||
def create_appmenus(self, verbose):
|
||||
if self.template_vm is not None:
|
||||
@ -1514,88 +1598,6 @@ class QubesAppVm(QubesCowVm):
|
||||
# Only add apps to menu
|
||||
subprocess.check_call ([qubes_appmenu_create_cmd, "none", self.name])
|
||||
|
||||
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"]:
|
||||
element = xml.etree.ElementTree.Element(
|
||||
"rule",
|
||||
address=rule["address"],
|
||||
port=str(rule["portBegin"]),
|
||||
)
|
||||
if rule["netmask"] is not None and rule["netmask"] != 32:
|
||||
element.set("netmask", str(rule["netmask"]))
|
||||
if rule["portEnd"] is not None:
|
||||
element.set("toport", str(rule["portEnd"]))
|
||||
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:
|
||||
print "{0}: save error: {1}".format(
|
||||
os.path.basename(sys.argv[0]), err)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
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 = {}
|
||||
attr_list = ("address", "netmask", "port", "toport")
|
||||
|
||||
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
|
||||
|
||||
rule["portBegin"] = int(rule["port"])
|
||||
|
||||
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
|
||||
|
||||
class QubesVmCollection(dict):
|
||||
"""
|
||||
@ -1896,8 +1898,8 @@ class QubesVmCollection(dict):
|
||||
kwargs = {}
|
||||
common_attr_list = ("qid", "name", "dir_path", "conf_file",
|
||||
"private_img", "root_img", "template_qid",
|
||||
"installed_by_rpm", "updateable",
|
||||
"uses_default_netvm", "label")
|
||||
"installed_by_rpm", "updateable", "internal",
|
||||
"uses_default_netvm", "label", "memory", "vcpus", "pcidevs")
|
||||
|
||||
for attribute in common_attr_list:
|
||||
kwargs[attribute] = element.get(attribute)
|
||||
@ -1909,6 +1911,9 @@ class QubesVmCollection(dict):
|
||||
if "installed_by_rpm" in kwargs:
|
||||
kwargs["installed_by_rpm"] = True if kwargs["installed_by_rpm"] == "True" else False
|
||||
|
||||
if "internal" in kwargs:
|
||||
kwargs["internal"] = True if kwargs["internal"] == "True" else False
|
||||
|
||||
if "template_qid" in kwargs:
|
||||
if kwargs["template_qid"] == "none" or kwargs["template_qid"] is None:
|
||||
kwargs.pop("template_qid")
|
||||
|
@ -127,6 +127,9 @@ def main():
|
||||
parser.add_option ("--skip-conflicting", action="store_true", dest="skip_conflicting", default=False,
|
||||
help="Do not restore VMs that are already present on the host")
|
||||
|
||||
parser.add_option ("--force-root", action="store_true", dest="force_root", default=False,
|
||||
help="Force to run, even with root privileges")
|
||||
|
||||
parser.add_option ("--recreate-conf-files", action="store_true", dest="recreate_conf", default=False,
|
||||
help="Recreate conf files after restore")
|
||||
|
||||
@ -242,14 +245,23 @@ def main():
|
||||
|
||||
print
|
||||
|
||||
if os.geteuid() == 0:
|
||||
print "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems."
|
||||
if options.force_root:
|
||||
print "Continuing as commanded. You have been warned."
|
||||
else:
|
||||
print "Retry as unprivileged user."
|
||||
print "... or use --force-root to continue anyway."
|
||||
exit(1)
|
||||
|
||||
if there_are_conflicting_vms:
|
||||
print "*** There VMs with conflicting names on the host! ***"
|
||||
if options.skip_conflicting:
|
||||
print "Those VMs will not be restored, the host VMs will not be overwritten!"
|
||||
else:
|
||||
print "Remove VMs with conflicting names from the host before proceeding."
|
||||
print "... or use --skip-conflicting to restore only those VMs that do not exist on the host."
|
||||
exit (1)
|
||||
if options.skip_conflicting:
|
||||
print "Those VMs will not be restored, the host VMs will not be overwritten!"
|
||||
else:
|
||||
print "Remove VMs with conflicting names from the host before proceeding."
|
||||
print "... or use --skip-conflicting to restore only those VMs that do not exist on the host."
|
||||
exit (1)
|
||||
|
||||
print "The above VMs will be copied and added to your system."
|
||||
print "Exisiting VMs will not be removed."
|
||||
|
@ -63,6 +63,8 @@ def main():
|
||||
help="Initial memory size (in MB)")
|
||||
parser.add_option ("-c", "--vcpus", dest="vcpus", default=None,
|
||||
help="VCPUs count")
|
||||
parser.add_option ("-i", "--internal", action="store_true", dest="internal", default=False,
|
||||
help="Create VM for internal use only (hidden in qubes-manager, no appmenus)")
|
||||
|
||||
parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
|
||||
(options, args) = parser.parse_args ()
|
||||
@ -134,6 +136,9 @@ def main():
|
||||
else:
|
||||
vm = qvm_collection.add_new_appvm(vmname, template_vm, label = label, updateable = options.standalone)
|
||||
|
||||
if options.internal:
|
||||
vm.internal = True
|
||||
|
||||
if options.mem is not None:
|
||||
vm.memory = options.mem
|
||||
|
||||
|
@ -124,6 +124,15 @@ def vm_run_cmd(vm, cmd, options):
|
||||
if options.tray:
|
||||
tray_notify_error ("ERROR: Cannot start the GUI daemon for this VM!")
|
||||
exit (1)
|
||||
|
||||
if not os.path.exists("/var/run/qubes/qrexec.{0}".format(xid)):
|
||||
retcode = subprocess.call ([qrexec_daemon_path, str(xid)])
|
||||
if (retcode != 0) :
|
||||
print "ERROR: Cannot start qrexec!"
|
||||
if options.tray:
|
||||
tray_notify_error ("ERROR: Cannot start the QRexec daemon for this VM!")
|
||||
exit (1)
|
||||
|
||||
actually_execute(str(xid), cmd, options);
|
||||
|
||||
def main():
|
||||
|
@ -32,7 +32,7 @@ DVMTMPLDIR="/var/lib/qubes/appvms/$DVMTMPL"
|
||||
if ! [ -d "$DVMTMPLDIR" ] ; then
|
||||
# unfortunately, currently there are reliability issues with save of a domain
|
||||
# with multiple CPUs
|
||||
if ! qvm-create --vcpus=1 -t "$TEMPLATENAME" -l gray "$DVMTMPL" ; then exit 1 ; fi
|
||||
if ! qvm-create --vcpus=1 --internal -t "$TEMPLATENAME" -l gray "$DVMTMPL" ; then exit 1 ; fi
|
||||
fi
|
||||
if ! /usr/lib/qubes/qubes_prepare_saved_domain.sh \
|
||||
"$DVMTMPL" "/var/lib/qubes/appvms/$DVMTMPL/dvm-savefile" $SCRIPTNAME ; then
|
||||
|
Loading…
Reference in New Issue
Block a user