# # CORE # Copyright (c)2011-2012 the Boeing Company. # See the LICENSE file included in this distribution. # ''' xen.py: implementation of the XenNode and XenVEth classes that support generating Xen domUs based on an ISO image and persistent configuration area ''' from core.netns.vnet import * from core.netns.vnode import LxcNode from core.coreobj import PyCoreObj, PyCoreNode, PyCoreNetIf from core.misc.ipaddr import * from core.misc.utils import * from core.constants import * from core.api import coreapi from core.netns.vif import TunTap from core.emane.nodes import EmaneNode try: import parted except ImportError, e: #print "Failed to load parted Python module required by Xen support." #print "Error was:", e raise ImportError import base64 import crypt import subprocess try: import fsimage except ImportError, e: # fix for fsimage under Ubuntu sys.path.append("/usr/lib/xen-default/lib/python") try: import fsimage except ImportError, e: #print "Failed to load fsimage Python module required by Xen support." #print "Error was:", e raise ImportError import os import time import shutil import string # 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" class XenVEth(PyCoreNetIf): def __init__(self, node, name, localname, mtu = 1500, net = None, start = True, hwaddr = None): # note that net arg is ignored 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) check_call(cmd) check_call([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): apitype = coreapi.CORE_NODE_XEN FilesToIgnore = frozenset([ #'ipforward.sh', 'quaggaboot.sh', ]) FilesRedirection = { 'ipforward.sh' : '/core-tmp/ipforward.sh', } CmdsToIgnore = frozenset([ #'sh ipforward.sh', #'sh quaggaboot.sh zebra', #'sh quaggaboot.sh ospfd', #'sh quaggaboot.sh ospf6d', 'sh quaggaboot.sh vtysh', 'killall zebra', 'killall ospfd', 'killall ospf6d', 'pidof zebra', 'pidof ospfd', 'pidof ospf6d', ]) def RedirCmd_ipforward(self): sysctlFile = 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=sysctlFile) p1.wait() sysctlFile.close() def RedirCmd_zebra(self): check_call([SED_PATH, '-i', '-e', 's/^zebra=no/zebra=yes/', os.path.join(self.mountdir, self.etcdir, 'quagga/daemons')]) def RedirCmd_ospfd(self): check_call([SED_PATH, '-i', '-e', 's/^ospfd=no/ospfd=yes/', os.path.join(self.mountdir, self.etcdir, 'quagga/daemons')]) def RedirCmd_ospf6d(self): check_call([SED_PATH, '-i', '-e', 's/^ospf6d=no/ospf6d=yes/', os.path.join(self.mountdir, self.etcdir, 'quagga/daemons')]) CmdsRedirection = { 'sh ipforward.sh' : RedirCmd_ipforward, 'sh quaggaboot.sh zebra' : RedirCmd_zebra, 'sh quaggaboot.sh ospfd' : RedirCmd_ospfd, 'sh quaggaboot.sh ospf6d' : RedirCmd_ospf6d, } # CoreNode: no __init__, take from LxcNode & SimpleLxcNode def __init__(self, session, objid = None, name = None, nodedir = None, bootsh = "boot.sh", verbose = False, start = True, model = None, vgname = None, ramsize = None, disksize = None, isofile = None): # SimpleLxcNode initialization PyCoreNode.__init__(self, session = session, objid = objid, name = name, verbose = verbose) 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.sessionid) + "-" + 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.FilesRedirection['/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.sessiondir, 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: raise Exception, "Xen PVM node requires a temporary nodedir" self.tmpnodedir = False self.bootsh = bootsh if start: self.startup() def getconfigitem(self, name, default=None): ''' 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): self.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() check_call([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): self.warn("XEN PVM boot() called") self.lock.acquire() if not self.up: 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: check_call([UMOUNT_BIN, self.mountdir]) self.unmount_all(self.mountdir) check_call([UDEVADM_PATH, 'settle']) check_call([KPARTX_PATH, '-d', self.lvpath]) #time.sleep(5) #time.sleep(1) # unpause VM if self.verbose: self.warn("XEN PVM boot() unpause domU %s" % self.vmname) mutecheck_call([XM_PATH, 'unpause', self.vmname]) self.booted = True finally: self.lock.release() def validate(self): self.session.services.validatenodeservices(self) # from class LxcNode (also SimpleLxcNode) def shutdown(self): self.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: mutecheck_call([XM_PATH, 'destroy', self.vmname]) self.booted = False except OSError: pass except subprocess.CalledProcessError: # ignore this error too, the VM may have exited already pass # discard LVM volume lvmRemoveCount = 0 while os.path.exists(self.lvpath): try: check_call([UDEVADM_PATH, 'settle']) mutecall([LVCHANGE_PATH, '-an', self.lvpath]) lvmRemoveCount += 1 mutecall([LVREMOVE_PATH, '-f', self.lvpath]) except OSError: pass if (lvmRemoveCount > 1): self.warn("XEN PVM shutdown() required %d lvremove " \ "executions." % lvmRemoveCount) self._netif.clear() del self.session self.up = False finally: self.rmnodedir() self.lock.release() def createlogicalvolume(self): ''' Create a logical volume for this Xen domU. Called from startup(). ''' if os.path.exists(self.lvpath): raise Exception, "LVM volume already exists" mutecheck_call([LVCREATE_PATH, '--size', self.disksize, '--name', self.lvname, self.vgname]) def createpartitions(self): ''' 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) persist_size = int(0.75 * constraint.maxSize); self.createpartition(device=dev, disk=disk, start=1, end=(persist_size - 1) , type="ext4") self.createpartition(device=dev, disk=disk, start=persist_size, end=(constraint.maxSize - 1) , type="linux-swap(v1)") disk.commit() def createpartition(self, device, disk, start, end, type): ''' 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) 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): ''' 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() check_call([KPARTX_PATH, '-a', self.lvpath]) mutecheck_call([MKFSEXT4_PATH, '-L', 'persist', persistdev]) mutecheck_call([MKSWAP_PATH, '-f', '-L', 'swap', swapdev]) return persistdev def untarpersistent(self, tarname, iso): ''' Unpack a persistent template tar file to the mounted mount dir. Uses fsimage library to read from an ISO file. ''' tarname = tarname.replace('%h', self.name) # filename may use hostname if iso: try: fs = fsimage.open(self.isofile, 0) except IOError, e: self.warn("Failed to open ISO file: %s (%s)" % (self.isofile,e)) return try: tardata = fs.open_file(tarname).read(); except IOError, e: self.warn("Failed to open tar file: %s (%s)" % (tarname, e)) return finally: del fs; else: try: f = open(tarname) tardata = f.read() f.close() except IOError, e: self.warn("Failed to open tar file: %s (%s)" % (tarname, e)) 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): ''' 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))) check_call([SED_PATH, '-i', '-e', '/^root:/s_^root:\([^:]*\):_root:' + saltedpw + ':_', os.path.join(self.mountdir, self.etcdir, 'shadow')]) def sethostname(self, old, new): ''' Set the hostname by updating the hostname and hosts files that reside on the filesystem mounted in the temporary area. ''' check_call([SED_PATH, '-i', '-e', 's/%s/%s/' % (old, new), os.path.join(self.mountdir, self.etcdir, 'hostname')]) check_call([SED_PATH, '-i', '-e', 's/%s/%s/' % (old, new), os.path.join(self.mountdir, self.etcdir, 'hosts')]) def setupssh(self, keypath): ''' 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') check_call([SED_PATH, '-i', '-e', 's/PermitRootLogin no/PermitRootLogin yes/', sshdcfg]) sshdir = os.path.join(self.getconfigitem('mount_path'), self.etcdir, 'ssh') sshdir = sshdir.replace('/','\\/') # backslash slashes for use in sed check_call([SED_PATH, '-i', '-e', 's/#AuthorizedKeysFile %h\/.ssh\/authorized_keys/' + \ 'AuthorizedKeysFile ' + sshdir + '\/authorized_keys/', 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): ''' 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')) mutecheck_call(args) # from class LxcNode def privatedir(self, path): #self.warn("XEN PVM privatedir() called") # Do nothing, Xen PVM nodes are fully private pass # from class LxcNode def opennodefile(self, filename, mode = "w"): self.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 def openpausednodefile(self, filename, mode = "w"): dirname, basename = os.path.split(filename) if not basename: raise ValueError, "no basename for filename: " + filename if dirname and dirname[0] == "/": dirname = dirname[1:] #dirname = dirname.replace("/", ".") dirname = os.path.join(self.nodedir, dirname) if not os.path.isdir(dirname): os.makedirs(dirname, mode = 0755) hostfilename = os.path.join(dirname, basename) return open(hostfilename, mode) # from class LxcNode def nodefile(self, filename, contents, mode = 0644): if filename in self.FilesToIgnore: #self.warn("XEN PVM nodefile(filename=%s) ignored" % [filename]) return if filename in self.FilesRedirection: redirFilename = self.FilesRedirection[filename] self.warn("XEN PVM nodefile(filename=%s) redirected to %s" % (filename, redirFilename)) filename = redirFilename self.warn("XEN PVM nodefile(filename=%s) called" % [filename]) self.lock.acquire() if not self.up: self.lock.release() raise Exception, "Can't access VM file as VM disk isn't ready" return if self.booted: self.lock.release() raise Exception, "Can't access VM file as VM is already running" return try: f = self.openpausednodefile(filename, "w") f.write(contents) os.chmod(f.name, mode) f.close() self.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode)) finally: self.lock.release() # from class SimpleLxcNode def alive(self): # is VM running? return False # XXX def cmd(self, args, wait = True): cmdAsString = string.join(args, ' ') if cmdAsString in self.CmdsToIgnore: #self.warn("XEN PVM cmd(args=[%s]) called and ignored" % cmdAsString) return 0 if cmdAsString in self.CmdsRedirection: self.CmdsRedirection[cmdAsString](self) return 0 self.warn("XEN PVM cmd(args=[%s]) called, but not yet implemented" % cmdAsString) return 0 def cmdresult(self, args): cmdAsString = string.join(args, ' ') if cmdAsString in self.CmdsToIgnore: #self.warn("XEN PVM cmd(args=[%s]) called and ignored" % cmdAsString) return (0, "") self.warn("XEN PVM cmdresult(args=[%s]) called, but not yet implemented" % cmdAsString) return (0, "") def popen(self, args): cmdAsString = string.join(args, ' ') self.warn("XEN PVM popen(args=[%s]) called, but not yet implemented" % cmdAsString) return def icmd(self, args): cmdAsString = string.join(args, ' ') self.warn("XEN PVM icmd(args=[%s]) called, but not yet implemented" % cmdAsString) return def term(self, sh = "/bin/sh"): self.warn("XEN PVM term() called, but not yet implemented") return def termcmdstring(self, sh = "/bin/sh"): ''' We may add 'sudo' to the command string because the GUI runs as a 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 == 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 F1: " #cmd += "string(\"root\\n\") \\n F2: string(\"%s\\n\")' " % pw cmd += "-e sudo %s console %s" % (XM_PATH, self.vmname) return cmd def shcmd(self, cmdstr, sh = "/bin/sh"): self.warn("XEN PVM shcmd(args=[%s]) called, but not yet implemented" % cmdstr) return # from class SimpleLxcNode def info(self, msg): if self.verbose: print "%s: %s" % (self.name, msg) sys.stdout.flush() # from class SimpleLxcNode def warn(self, msg): print >> sys.stderr, "%s: %s" % (self.name, msg) sys.stderr.flush() def mount(self, source, target): self.warn("XEN PVM Nodes can't bind-mount filesystems") def umount(self, target): self.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): self.warn("XEN PVM addnetif() called") PyCoreNode.addnetif(self, netif, ifindex) def delnetif(self, ifindex): self.warn("XEN PVM delnetif() called") PyCoreNode.delnetif(self, ifindex) def newveth(self, ifindex = None, ifname = None, net = None, hwaddr = None): self.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 sessionid = self.session.shortsessionid() name = "n%s.%s.%s" % (self.objid, ifindex, sessionid) localname = "n%s.%s.%s" % (self.objid, ifname, sessionid) ifclass = XenVEth 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() def newtuntap(self, ifindex = None, ifname = None, net = None): self.warn("XEN PVM newtuntap() called but not implemented") def sethwaddr(self, ifindex, addr): self._netif[ifindex].sethwaddr(addr) if self.up: pass #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: self.warn("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") 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 self.getaddr(self.ifname(ifindex), rescan = True) # Xen PVM relies on boot process to bring up links #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): self.warn("XEN PVM newnetif(ifindex=%s, ifname=%s) called" % (ifindex, ifname)) self.lock.acquire() if not self.up: self.lock.release() raise Exception, "Can't access add veth as VM disk isn't ready" return if self.booted: self.lock.release() raise Exception, "Can't access add veth as VM is already running" return try: if isinstance(net, EmaneNode): 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: 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") 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. 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) for addr in maketuple(addrlist): self.addaddr(ifindex, addr) #self.ifup(ifindex) return ifindex finally: self.lock.release() def connectnode(self, ifname, othernode, otherifname): self.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): self.lock.acquire() if not self.up: self.lock.release() raise Exception, "Can't access VM file as VM disk isn't ready" return if self.booted: self.lock.release() raise Exception, "Can't access VM file as VM is already running" return if filename in self.FilesToIgnore: #self.warn("XEN PVM addfile(filename=%s) ignored" % [filename]) return if filename in self.FilesRedirection: redirFilename = self.FilesRedirection[filename] self.warn("XEN PVM addfile(filename=%s) redirected to %s" % (filename, redirFilename)) filename = redirFilename 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() self.info("created nodefile: '%s'; mode: 0%o" % (fout.name, mode)) finally: self.lock.release() self.warn("XEN PVM addfile(filename=%s) called" % [filename]) #shcmd = "mkdir -p $(dirname '%s') && mv '%s' '%s' && sync" % \ # (filename, srcname, filename) #self.shcmd(shcmd) def unmount_all(self, path): ''' 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 for o in self.session.objs(): if not isinstance(o, LxcNode): continue o.umount(path)