core-extra/daemon/core/xen/xen.py

789 lines
30 KiB
Python
Raw Normal View History

2017-06-19 18:09:28 -07:00
"""
xen.py: implementation of the XenNode and XenVEth classes that support
generating Xen domUs based on an ISO image and persistent configuration area
2017-06-19 18:09:28 -07:00
"""
2017-06-19 18:09:28 -07:00
import base64
import os
import shutil
import string
import subprocess
import sys
import threading
import crypt
from core import constants
from core import logger
2017-06-19 18:09:28 -07:00
from core.coreobj import PyCoreNetIf
from core.coreobj import PyCoreNode
from core.enumerations import NodeTypes
from core.misc import nodeutils
from core.misc import utils
from core.netns.vnode import LxcNode
2017-06-19 18:09:28 -07:00
try:
import parted
2017-06-19 18:09:28 -07:00
except ImportError:
logger.error("failed to import parted for xen nodes")
try:
import fsimage
2017-06-19 18:09:28 -07:00
except ImportError:
# fix for fsimage under Ubuntu
sys.path.append("/usr/lib/xen-default/lib/python")
try:
import fsimage
2017-06-19 18:09:28 -07:00
except ImportError:
logger.error("failed to import fsimage for xen nodes")
# XXX move these out to config file
AWK_PATH = "/bin/awk"
KPARTX_PATH = "/sbin/kpartx"
LVCREATE_PATH = "/sbin/lvcreate"
LVREMOVE_PATH = "/sbin/lvremove"
LVCHANGE_PATH = "/sbin/lvchange"
MKFSEXT4_PATH = "/sbin/mkfs.ext4"
MKSWAP_PATH = "/sbin/mkswap"
TAR_PATH = "/bin/tar"
SED_PATH = "/bin/sed"
XM_PATH = "/usr/sbin/xm"
UDEVADM_PATH = "/sbin/udevadm"
2017-06-19 18:09:28 -07:00
class XenVEth(PyCoreNetIf):
def __init__(self, node, name, localname, mtu=1500, net=None, start=True, hwaddr=None):
# note that net arg is ignored
2017-06-19 18:09:28 -07:00
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
self.localname = localname
self.up = False
self.hwaddr = hwaddr
if start:
self.startup()
def startup(self):
cmd = [XM_PATH, "network-attach", self.node.vmname, "vifname=%s" % self.localname, "script=vif-core"]
if self.hwaddr is not None:
cmd.append("mac=%s" % self.hwaddr)
2017-06-19 18:09:28 -07:00
subprocess.check_call(cmd)
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "up"])
self.up = True
def shutdown(self):
if not self.up:
return
if self.localname:
if self.hwaddr is not None:
pass
# this should be doable, but some argument isn"t a string
# check_call([XM_PATH, "network-detach", self.node.vmname,
# self.hwaddr])
self.up = False
class XenNode(PyCoreNode):
2017-06-19 18:09:28 -07:00
apitype = NodeTypes.XEN.value
files_to_ignore = frozenset([
# "ipforward.sh",
"quaggaboot.sh",
])
files_redirection = {
"ipforward.sh": "/core-tmp/ipforward.sh",
}
cmds_to_ignore = frozenset([
# "sh ipforward.sh",
# "sh quaggaboot.sh zebra",
# "sh quaggaboot.sh ospfd",
# "sh quaggaboot.sh ospf6d",
"killall zebra",
"killall ospfd",
"killall ospf6d",
"pidof zebra", "pidof ospfd", "pidof ospf6d",
])
def redir_cmd_ipforward(self):
sysctl_file = open(os.path.join(self.mountdir, self.etcdir, "sysctl.conf"), "a")
p1 = subprocess.Popen([AWK_PATH, "/^\/sbin\/sysctl -w/ {print $NF}",
os.path.join(self.nodedir, "core-tmp/ipforward.sh")], stdout=sysctl_file)
p1.wait()
sysctl_file.close()
def redir_cmd_zebra(self):
subprocess.check_call([SED_PATH, "-i", "-e", "s/^zebra=no/zebra=yes/",
os.path.join(self.mountdir, self.etcdir, "quagga/daemons")])
def redir_cmd_ospfd(self):
subprocess.check_call([SED_PATH, "-i", "-e", "s/^ospfd=no/ospfd=yes/",
os.path.join(self.mountdir, self.etcdir, "quagga/daemons")])
def redir_cmd_ospf6d(self):
subprocess.check_call([SED_PATH, "-i", "-e", "s/^ospf6d=no/ospf6d=yes/",
os.path.join(self.mountdir, self.etcdir, "quagga/daemons")])
cmds_redirection = {
"sh ipforward.sh": redir_cmd_ipforward,
"sh quaggaboot.sh zebra": redir_cmd_zebra,
"sh quaggaboot.sh ospfd": redir_cmd_ospfd,
"sh quaggaboot.sh ospf6d": redir_cmd_ospf6d,
}
# CoreNode: no __init__, take from LxcNode & SimpleLxcNode
2017-06-19 18:09:28 -07:00
def __init__(self, session, objid=None, name=None,
nodedir=None, bootsh="boot.sh", start=True, model=None,
vgname=None, ramsize=None, disksize=None,
isofile=None):
# SimpleLxcNode initialization
2017-06-19 18:09:28 -07:00
PyCoreNode.__init__(self, session=session, objid=objid, name=name)
self.nodedir = nodedir
self.model = model
# indicates startup() has been invoked and disk has been initialized
self.up = False
# indicates boot() has been invoked and domU is running
self.booted = False
self.ifindex = 0
self.lock = threading.RLock()
self._netif = {}
# domU name
self.vmname = "c" + str(session.session_id) + "-" + name
# LVM volume group name
self.vgname = self.getconfigitem("vg_name", vgname)
# LVM logical volume name
self.lvname = self.vmname + "-"
# LVM logical volume device path name
self.lvpath = os.path.join("/dev", self.vgname, self.lvname)
self.disksize = self.getconfigitem("disk_size", disksize)
self.ramsize = int(self.getconfigitem("ram_size", ramsize))
self.isofile = self.getconfigitem("iso_file", isofile)
# temporary mount point for paused VM persistent filesystem
self.mountdir = None
self.etcdir = self.getconfigitem("etc_path")
# TODO: remove this temporary hack
self.files_redirection["/usr/local/etc/quagga/Quagga.conf"] = os.path.join(
self.getconfigitem("mount_path"), self.etcdir, "quagga/Quagga.conf")
# LxcNode initialization
# self.makenodedir()
if self.nodedir is None:
self.nodedir = os.path.join(session.session_dir, self.name + ".conf")
self.mountdir = self.nodedir + self.getconfigitem("mount_path")
if not os.path.isdir(self.mountdir):
os.makedirs(self.mountdir)
self.tmpnodedir = True
else:
2017-06-19 18:09:28 -07:00
raise Exception("Xen PVM node requires a temporary nodedir")
self.bootsh = bootsh
if start:
self.startup()
def getconfigitem(self, name, default=None):
2017-06-19 18:09:28 -07:00
"""
Configuration items come from the xen.conf file and/or input from
the GUI, and are stored in the session using the XenConfigManager
object. self.model is used to identify particular profiles
associated with a node type in the GUI.
"""
return self.session.xen.getconfigitem(name=name, model=self.model, node=self, value=default)
# from class LxcNode (also SimpleLxcNode)
def startup(self):
logger.warn("XEN PVM startup() called: preparing disk for %s", self.name)
self.lock.acquire()
try:
if self.up:
raise Exception("already up")
self.createlogicalvolume()
self.createpartitions()
persistdev = self.createfilesystems()
subprocess.check_call([constants.MOUNT_BIN, "-t", "ext4", persistdev, self.mountdir])
self.untarpersistent(tarname=self.getconfigitem("persist_tar_iso"),
iso=True)
self.setrootpassword(pw=self.getconfigitem("root_password"))
self.sethostname(old="UBASE", new=self.name)
self.setupssh(keypath=self.getconfigitem("ssh_key_path"))
self.createvm()
self.up = True
finally:
self.lock.release()
# from class LxcNode (also SimpleLxcNode)
def boot(self):
2017-06-19 18:09:28 -07:00
logger.warn("XEN PVM boot() called")
self.lock.acquire()
if not self.up:
2017-06-19 18:09:28 -07:00
raise Exception("Can't boot VM without initialized disk")
if self.booted:
self.lock.release()
return
self.session.services.bootnodeservices(self)
tarname = self.getconfigitem("persist_tar")
if tarname:
self.untarpersistent(tarname=tarname, iso=False)
try:
2017-06-19 18:09:28 -07:00
subprocess.check_call([constants.UMOUNT_BIN, self.mountdir])
self.unmount_all(self.mountdir)
subprocess.check_call([UDEVADM_PATH, "settle"])
subprocess.check_call([KPARTX_PATH, "-d", self.lvpath])
2017-06-19 18:09:28 -07:00
# time.sleep(5)
# time.sleep(1)
# unpause VM
logger.warn("XEN PVM boot() unpause domU %s", self.vmname)
utils.mutecheck_call([XM_PATH, "unpause", self.vmname])
self.booted = True
finally:
self.lock.release()
def validate(self):
self.session.services.validatenodeservices(self)
2017-06-19 18:09:28 -07:00
# from class LxcNode (also SimpleLxcNode)
def shutdown(self):
2017-06-19 18:09:28 -07:00
logger.warn("XEN PVM shutdown() called")
if not self.up:
return
self.lock.acquire()
try:
if self.up:
# sketch from SimpleLxcNode
for netif in self.netifs():
netif.shutdown()
try:
# RJE XXX what to do here
if self.booted:
utils.mutecheck_call([XM_PATH, "destroy", self.vmname])
self.booted = False
2017-06-19 18:09:28 -07:00
except (OSError, subprocess.CalledProcessError):
# ignore this error too, the VM may have exited already
2017-06-19 18:09:28 -07:00
logger.exception("error during shutdown")
# discard LVM volume
lvm_remove_count = 0
while os.path.exists(self.lvpath):
try:
subprocess.check_call([UDEVADM_PATH, "settle"])
utils.mutecall([LVCHANGE_PATH, "-an", self.lvpath])
lvm_remove_count += 1
utils.mutecall([LVREMOVE_PATH, "-f", self.lvpath])
except OSError:
2017-06-19 18:09:28 -07:00
logger.exception("error during shutdown")
if lvm_remove_count > 1:
logger.warn("XEN PVM shutdown() required %d lvremove executions.", lvm_remove_count)
self._netif.clear()
del self.session
self.up = False
finally:
self.rmnodedir()
self.lock.release()
def createlogicalvolume(self):
2017-06-19 18:09:28 -07:00
"""
Create a logical volume for this Xen domU. Called from startup().
"""
if os.path.exists(self.lvpath):
raise Exception, "LVM volume already exists"
utils.mutecheck_call([LVCREATE_PATH, "--size", self.disksize,
"--name", self.lvname, self.vgname])
def createpartitions(self):
2017-06-19 18:09:28 -07:00
"""
Partition the LVM volume into persistent and swap partitions
using the parted module.
"""
dev = parted.Device(path=self.lvpath)
dev.removeFromCache()
disk = parted.freshDisk(dev, "msdos")
constraint = parted.Constraint(device=dev)
2017-06-19 18:09:28 -07:00
persist_size = int(0.75 * constraint.maxSize)
self.createpartition(device=dev, disk=disk, start=1,
2017-06-19 18:09:28 -07:00
end=persist_size - 1, type="ext4")
self.createpartition(device=dev, disk=disk, start=persist_size,
2017-06-19 18:09:28 -07:00
end=constraint.maxSize - 1, type="linux-swap(v1)")
disk.commit()
def createpartition(self, device, disk, start, end, type):
2017-06-19 18:09:28 -07:00
"""
Create a single partition of the specified type and size and add
it to the disk object, using the parted module.
"""
geo = parted.Geometry(device=device, start=start, end=end)
fs = parted.FileSystem(type=type, geometry=geo)
2017-06-19 18:09:28 -07:00
part = parted.Partition(disk=disk, fs=fs, type=parted.PARTITION_NORMAL, geometry=geo)
constraint = parted.Constraint(exactGeom=geo)
disk.addPartition(partition=part, constraint=constraint)
def createfilesystems(self):
2017-06-19 18:09:28 -07:00
"""
Make an ext4 filesystem and swap space. Return the device name for
the persistent partition so we can mount it.
"""
output = subprocess.Popen([KPARTX_PATH, "-l", self.lvpath],
stdout=subprocess.PIPE).communicate()[0]
lines = output.splitlines()
persistdev = "/dev/mapper/" + lines[0].strip().split(" ")[0].strip()
swapdev = "/dev/mapper/" + lines[1].strip().split(" ")[0].strip()
subprocess.check_call([KPARTX_PATH, "-a", self.lvpath])
utils.mutecheck_call([MKFSEXT4_PATH, "-L", "persist", persistdev])
utils.mutecheck_call([MKSWAP_PATH, "-f", "-L", "swap", swapdev])
return persistdev
def untarpersistent(self, tarname, iso):
2017-06-19 18:09:28 -07:00
"""
Unpack a persistent template tar file to the mounted mount dir.
Uses fsimage library to read from an ISO file.
"""
# filename may use hostname
tarname = tarname.replace("%h", self.name)
if iso:
try:
fs = fsimage.open(self.isofile, 0)
2017-06-19 18:09:28 -07:00
except IOError:
logger.exception("Failed to open ISO file: %s", self.isofile)
return
try:
2017-06-19 18:09:28 -07:00
tardata = fs.open_file(tarname).read()
except IOError:
logger.exception("Failed to open tar file: %s", tarname)
return
finally:
2017-06-19 18:09:28 -07:00
del fs
else:
try:
f = open(tarname)
tardata = f.read()
f.close()
2017-06-19 18:09:28 -07:00
except IOError:
logger.exception("Failed to open tar file: %s", tarname)
return
p = subprocess.Popen([TAR_PATH, "-C", self.mountdir, "--numeric-owner",
"-xf", "-"], stdin=subprocess.PIPE)
p.communicate(input=tardata)
p.wait()
def setrootpassword(self, pw):
2017-06-19 18:09:28 -07:00
"""
Set the root password by updating the shadow password file that
is on the filesystem mounted in the temporary area.
"""
saltedpw = crypt.crypt(pw, "$6$" + base64.b64encode(os.urandom(12)))
subprocess.check_call([SED_PATH, "-i", "-e",
"/^root:/s_^root:\([^:]*\):_root:" + saltedpw + ":_",
os.path.join(self.mountdir, self.etcdir, "shadow")])
def sethostname(self, old, new):
2017-06-19 18:09:28 -07:00
"""
Set the hostname by updating the hostname and hosts files that
reside on the filesystem mounted in the temporary area.
"""
subprocess.check_call([SED_PATH, "-i", "-e", "s/%s/%s/" % (old, new),
os.path.join(self.mountdir, self.etcdir, "hostname")])
subprocess.check_call([SED_PATH, "-i", "-e", "s/%s/%s/" % (old, new),
os.path.join(self.mountdir, self.etcdir, "hosts")])
def setupssh(self, keypath):
2017-06-19 18:09:28 -07:00
"""
Configure SSH access by installing host keys and a system-wide
authorized_keys file.
"""
sshdcfg = os.path.join(self.mountdir, self.etcdir, "ssh/sshd_config")
subprocess.check_call([SED_PATH, "-i", "-e", "s/PermitRootLogin no/PermitRootLogin yes/", sshdcfg])
sshdir = os.path.join(self.getconfigitem("mount_path"), self.etcdir, "ssh")
# backslash slashes for use in sed
sshdir = sshdir.replace("/", "\\/")
subprocess.check_call([SED_PATH, "-i", "-e",
"s/#AuthorizedKeysFile %h\/.ssh\/authorized_keys/" + \
"AuthorizedKeysFile " + sshdir + "\/authorized_keys/",
2017-06-19 18:09:28 -07:00
sshdcfg])
for f in "ssh_host_rsa_key", "ssh_host_rsa_key.pub", "authorized_keys":
src = os.path.join(keypath, f)
dst = os.path.join(self.mountdir, self.etcdir, "ssh", f)
shutil.copy(src, dst)
if f[-3:] != "pub":
os.chmod(dst, 0600)
def createvm(self):
2017-06-19 18:09:28 -07:00
"""
Instantiate a *paused* domU VM
Instantiate it now, so we can add network interfaces,
pause it so we can have the filesystem open for configuration.
"""
args = [XM_PATH, "create", os.devnull, "--paused"]
args.extend(["name=" + self.vmname, "memory=" + str(self.ramsize)])
args.append("disk=tap:aio:" + self.isofile + ",hda,r")
args.append("disk=phy:" + self.lvpath + ",hdb,w")
args.append("bootloader=pygrub")
bootargs = "--kernel=/isolinux/vmlinuz --ramdisk=/isolinux/initrd"
args.append("bootargs=" + bootargs)
for action in ("poweroff", "reboot", "suspend", "crash", "halt"):
args.append("on_%s=destroy" % action)
args.append("extra=" + self.getconfigitem("xm_create_extra"))
2017-06-19 18:09:28 -07:00
utils.mutecheck_call(args)
# from class LxcNode
def privatedir(self, path):
2017-06-19 18:09:28 -07:00
# self.warn("XEN PVM privatedir() called")
# Do nothing, Xen PVM nodes are fully private
pass
# from class LxcNode
2017-06-19 18:09:28 -07:00
def opennodefile(self, filename, mode="w"):
logger.warn("XEN PVM opennodefile() called")
raise Exception("Can't open VM file with opennodefile()")
# from class LxcNode
# open a file on a paused Xen node
2017-06-19 18:09:28 -07:00
def openpausednodefile(self, filename, mode="w"):
dirname, basename = os.path.split(filename)
if not basename:
raise ValueError("no basename for filename: %s" % filename)
if dirname and dirname[0] == "/":
dirname = dirname[1:]
2017-06-19 18:09:28 -07:00
# dirname = dirname.replace("/", ".")
dirname = os.path.join(self.nodedir, dirname)
if not os.path.isdir(dirname):
2017-06-19 18:09:28 -07:00
os.makedirs(dirname, mode=0755)
hostfilename = os.path.join(dirname, basename)
return open(hostfilename, mode)
# from class LxcNode
2017-06-19 18:09:28 -07:00
def nodefile(self, filename, contents, mode=0644):
if filename in self.files_to_ignore:
2017-06-19 18:09:28 -07:00
# self.warn("XEN PVM nodefile(filename=%s) ignored" % [filename])
return
if filename in self.files_redirection:
redirection_filename = self.files_redirection[filename]
logger.warn("XEN PVM nodefile(filename=%s) redirected to %s", filename, redirection_filename)
filename = redirection_filename
2017-06-19 18:09:28 -07:00
logger.warn("XEN PVM nodefile(filename=%s) called", filename)
self.lock.acquire()
if not self.up:
self.lock.release()
2017-06-19 18:09:28 -07:00
raise Exception("Can't access VM file as VM disk isn't ready")
if self.booted:
self.lock.release()
2017-06-19 18:09:28 -07:00
raise Exception("Can't access VM file as VM is already running")
try:
f = self.openpausednodefile(filename, "w")
f.write(contents)
os.chmod(f.name, mode)
f.close()
logger.info("created nodefile: %s; mode: 0%o", f.name, mode)
finally:
self.lock.release()
# from class SimpleLxcNode
def alive(self):
# is VM running?
2017-06-19 18:09:28 -07:00
return False # XXX
2017-06-19 18:09:28 -07:00
def cmd(self, args, wait=True):
cmd_string = string.join(args, " ")
if cmd_string in self.cmds_to_ignore:
2017-06-19 18:09:28 -07:00
# self.warn("XEN PVM cmd(args=[%s]) called and ignored" % cmdAsString)
return 0
if cmd_string in self.cmds_redirection:
self.cmds_redirection[cmd_string](self)
return 0
logger("XEN PVM cmd(args=[%s]) called, but not yet implemented", cmd_string)
2017-06-19 18:09:28 -07:00
return 0
def cmdresult(self, args):
cmd_string = string.join(args, " ")
if cmd_string in self.cmds_to_ignore:
2017-06-19 18:09:28 -07:00
# self.warn("XEN PVM cmd(args=[%s]) called and ignored" % cmdAsString)
return 0, ""
logger.warn("XEN PVM cmdresult(args=[%s]) called, but not yet implemented", cmd_string)
2017-06-19 18:09:28 -07:00
return 0, ""
def popen(self, args):
cmd_string = string.join(args, " ")
logger.warn("XEN PVM popen(args=[%s]) called, but not yet implemented", cmd_string)
return
def icmd(self, args):
cmd_string = string.join(args, " ")
logger.warn("XEN PVM icmd(args=[%s]) called, but not yet implemented", cmd_string)
return
2017-06-19 18:09:28 -07:00
def term(self, sh="/bin/sh"):
logger.warn("XEN PVM term() called, but not yet implemented")
return
2017-06-19 18:09:28 -07:00
def termcmdstring(self, sh="/bin/sh"):
"""
We may add "sudo" to the command string because the GUI runs as a
2017-06-19 18:09:28 -07:00
normal user. Use SSH if control interface is available, otherwise
use Xen console with a keymapping for easy login.
"""
controlifc = None
for ifc in self.netifs():
if hasattr(ifc, "control") and ifc.control is True:
controlifc = ifc
break
cmd = "xterm "
# use SSH if control interface is available
if controlifc:
controlip = controlifc.addrlist[0].split("/")[0]
cmd += "-e ssh root@%s" % controlip
return cmd
# otherwise use "xm console"
# pw = self.getconfigitem("root_password")
# cmd += "-xrm "XTerm*VT100.translations: #override <Key>F1: "
# cmd += "string(\"root\\n\") \\n <Key>F2: string(\"%s\\n\")" " % pw
cmd += "-e sudo %s console %s" % (XM_PATH, self.vmname)
return cmd
2017-06-19 18:09:28 -07:00
def shcmd(self, cmdstr, sh="/bin/sh"):
logger("XEN PVM shcmd(args=[%s]) called, but not yet implemented", cmdstr)
return
def mount(self, source, target):
2017-06-19 18:09:28 -07:00
logger.warn("XEN PVM Nodes can't bind-mount filesystems")
def umount(self, target):
2017-06-19 18:09:28 -07:00
logger.warn("XEN PVM Nodes can't bind-mount filesystems")
def newifindex(self):
self.lock.acquire()
try:
while self.ifindex in self._netif:
self.ifindex += 1
ifindex = self.ifindex
self.ifindex += 1
return ifindex
finally:
self.lock.release()
def getifindex(self, netif):
for ifindex in self._netif:
if self._netif[ifindex] is netif:
return ifindex
return -1
def addnetif(self, netif, ifindex):
2017-06-19 18:09:28 -07:00
logger.warn("XEN PVM addnetif() called")
PyCoreNode.addnetif(self, netif, ifindex)
def delnetif(self, ifindex):
2017-06-19 18:09:28 -07:00
logger.warn("XEN PVM delnetif() called")
PyCoreNode.delnetif(self, ifindex)
2017-06-19 18:09:28 -07:00
def newveth(self, ifindex=None, ifname=None, net=None, hwaddr=None):
logger.warn("XEN PVM newveth(ifindex=%s, ifname=%s) called", ifindex, ifname)
self.lock.acquire()
try:
if ifindex is None:
ifindex = self.newifindex()
if ifname is None:
ifname = "eth%d" % ifindex
2017-06-19 18:09:28 -07:00
sessionid = self.session.short_session_id()
name = "n%s.%s.%s" % (self.objid, ifindex, sessionid)
localname = "n%s.%s.%s" % (self.objid, ifname, sessionid)
ifclass = XenVEth
2017-06-19 18:09:28 -07:00
veth = ifclass(node=self, name=name, localname=localname,
mtu=1500, net=net, hwaddr=hwaddr)
veth.name = ifname
try:
self.addnetif(veth, ifindex)
except:
veth.shutdown()
del veth
raise
return ifindex
finally:
self.lock.release()
2017-06-19 18:09:28 -07:00
def newtuntap(self, ifindex=None, ifname=None, net=None):
logger.warn("XEN PVM newtuntap() called but not implemented")
def sethwaddr(self, ifindex, addr):
self._netif[ifindex].sethwaddr(addr)
if self.up:
pass
2017-06-19 18:09:28 -07:00
# self.cmd([IP_BIN, "link", "set", "dev", self.ifname(ifindex),
# "address", str(addr)])
def addaddr(self, ifindex, addr):
if self.up:
pass
# self.cmd([IP_BIN, "addr", "add", str(addr),
# "dev", self.ifname(ifindex)])
self._netif[ifindex].addaddr(addr)
def deladdr(self, ifindex, addr):
try:
self._netif[ifindex].deladdr(addr)
except ValueError:
2017-06-19 18:09:28 -07:00
logger.exception("trying to delete unknown address: %s", addr)
if self.up:
pass
# self.cmd([IP_BIN, "addr", "del", str(addr),
# "dev", self.ifname(ifindex)])
valid_deladdrtype = ("inet", "inet6", "inet6link")
2017-06-19 18:09:28 -07:00
def delalladdr(self, ifindex, addrtypes=valid_deladdrtype):
addr = self.getaddr(self.ifname(ifindex), rescan=True)
for t in addrtypes:
if t not in self.valid_deladdrtype:
raise ValueError("addr type must be in: " + " ".join(self.valid_deladdrtype))
for a in addr[t]:
self.deladdr(ifindex, a)
# update cached information
2017-06-19 18:09:28 -07:00
self.getaddr(self.ifname(ifindex), rescan=True)
# Xen PVM relies on boot process to bring up links
2017-06-19 18:09:28 -07:00
# def ifup(self, ifindex):
# if self.up:
# self.cmd([IP_BIN, "link", "set", self.ifname(ifindex), "up"])
def newnetif(self, net=None, addrlist=[], hwaddr=None, ifindex=None, ifname=None):
logger.warn("XEN PVM newnetif(ifindex=%s, ifname=%s) called", ifindex, ifname)
self.lock.acquire()
if not self.up:
self.lock.release()
2017-06-19 18:09:28 -07:00
raise Exception("Can't access add veth as VM disk isn't ready")
if self.booted:
self.lock.release()
2017-06-19 18:09:28 -07:00
raise Exception("Can't access add veth as VM is already running")
try:
2017-06-19 18:09:28 -07:00
if nodeutils.is_node(net, NodeTypes.EMANE):
raise Exception("Xen PVM doesn't yet support Emane nets")
# ifindex = self.newtuntap(ifindex = ifindex, ifname = ifname,
# net = net)
# # TUN/TAP is not ready for addressing yet; the device may
# # take some time to appear, and installing it into a
# # namespace after it has been bound removes addressing;
# # save addresses with the interface now
# self.attachnet(ifindex, net)
# netif = self.netif(ifindex)
# netif.sethwaddr(hwaddr)
# for addr in maketuple(addrlist):
# netif.addaddr(addr)
# return ifindex
else:
2017-06-19 18:09:28 -07:00
ifindex = self.newveth(ifindex=ifindex, ifname=ifname,
net=net, hwaddr=hwaddr)
if net is not None:
self.attachnet(ifindex, net)
rulefile = os.path.join(self.getconfigitem("mount_path"),
self.etcdir,
"udev/rules.d/70-persistent-net.rules")
f = self.openpausednodefile(rulefile, "a")
2017-06-19 18:09:28 -07:00
f.write(
"\n# Xen PVM virtual interface #%s %s with MAC address %s\n" % (ifindex, self.ifname(ifindex), hwaddr))
# Using MAC address as we"re now loading PVM net driver "early"
# OLD: Would like to use MAC address, but udev isn"t working with paravirtualized NICs. Perhaps the "set hw address" isn"t triggering a rescan.
2017-06-19 18:09:28 -07:00
f.write(
'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="%s", KERNEL=="eth*", NAME="%s"\n' % (
hwaddr, self.ifname(ifindex)))
# f.write("SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", DEVPATH=="/devices/vif-%s/?*", KERNEL=="eth*", NAME="%s"\n" % (ifindex, self.ifname(ifindex)))
f.close()
if hwaddr:
self.sethwaddr(ifindex, hwaddr)
2017-06-19 18:09:28 -07:00
for addr in utils.maketuple(addrlist):
self.addaddr(ifindex, addr)
2017-06-19 18:09:28 -07:00
# self.ifup(ifindex)
return ifindex
finally:
self.lock.release()
def connectnode(self, ifname, othernode, otherifname):
2017-06-19 18:09:28 -07:00
logger.warn("XEN PVM connectnode() called")
# tmplen = 8
# tmp1 = "tmp." + "".join([random.choice(string.ascii_lowercase)
# for x in xrange(tmplen)])
# tmp2 = "tmp." + "".join([random.choice(string.ascii_lowercase)
# for x in xrange(tmplen)])
# check_call([IP_BIN, "link", "add", "name", tmp1,
# "type", "veth", "peer", "name", tmp2])
#
# check_call([IP_BIN, "link", "set", tmp1, "netns", str(self.pid)])
# self.cmd([IP_BIN, "link", "set", tmp1, "name", ifname])
# self.addnetif(PyCoreNetIf(self, ifname), self.newifindex())
#
# check_call([IP_BIN, "link", "set", tmp2, "netns", str(othernode.pid)])
# othernode.cmd([IP_BIN, "link", "set", tmp2, "name", otherifname])
# othernode.addnetif(PyCoreNetIf(othernode, otherifname),
# othernode.newifindex())
def addfile(self, srcname, filename, mode=0644):
self.lock.acquire()
if not self.up:
self.lock.release()
2017-06-19 18:09:28 -07:00
raise Exception("Can't access VM file as VM disk isn't ready")
if self.booted:
self.lock.release()
2017-06-19 18:09:28 -07:00
raise Exception("Can't access VM file as VM is already running")
if filename in self.files_to_ignore:
2017-06-19 18:09:28 -07:00
# self.warn("XEN PVM addfile(filename=%s) ignored" % [filename])
return
if filename in self.files_redirection:
redirection_filename = self.files_redirection[filename]
logger.warn("XEN PVM addfile(filename=%s) redirected to %s", filename, redirection_filename)
filename = redirection_filename
try:
fin = open(srcname, "r")
contents = fin.read()
fin.close()
fout = self.openpausednodefile(filename, "w")
fout.write(contents)
os.chmod(fout.name, mode)
fout.close()
logger.info("created nodefile: %s; mode: 0%o", fout.name, mode)
finally:
self.lock.release()
logger.warn("XEN PVM addfile(filename=%s) called", filename)
# shcmd = "mkdir -p $(dirname "%s") && mv "%s" "%s" && sync" % \
# (filename, srcname, filename)
2017-06-19 18:09:28 -07:00
# self.shcmd(shcmd)
def unmount_all(self, path):
2017-06-19 18:09:28 -07:00
"""
Namespaces inherit the host mounts, so we need to ensure that all
namespaces have unmounted our temporary mount area so that the
kpartx command will succeed.
"""
# Session.bootnodes() already has self.session._objslock
2017-06-19 18:09:28 -07:00
for o in self.session.objects.itervalues():
if not isinstance(o, LxcNode):
continue
o.umount(path)