merged cleanup branch with master
This commit is contained in:
parent
0a91fe7a3e
commit
55a6e2dcef
81 changed files with 11596 additions and 15021 deletions
|
@ -1,51 +1,43 @@
|
|||
#
|
||||
# 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
|
||||
"""
|
||||
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 *
|
||||
import base64
|
||||
import os
|
||||
import shutil
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
|
||||
import crypt
|
||||
|
||||
from core import constants
|
||||
from core.coreobj import PyCoreNetIf
|
||||
from core.coreobj import PyCoreNode
|
||||
from core.enumerations import NodeTypes
|
||||
from core.misc import log
|
||||
from core.misc import nodeutils
|
||||
from core.misc import utils
|
||||
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
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
try:
|
||||
import parted
|
||||
except ImportError, e:
|
||||
#print "Failed to load parted Python module required by Xen support."
|
||||
#print "Error was:", e
|
||||
raise ImportError
|
||||
except ImportError:
|
||||
logger.error("failed to import parted for xen nodes")
|
||||
|
||||
import base64
|
||||
import crypt
|
||||
import subprocess
|
||||
try:
|
||||
import fsimage
|
||||
except ImportError, e:
|
||||
except ImportError:
|
||||
# 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
|
||||
except ImportError:
|
||||
logger.error("failed to import fsimage for xen nodes")
|
||||
|
||||
# XXX move these out to config file
|
||||
AWK_PATH = "/bin/awk"
|
||||
|
@ -60,11 +52,12 @@ 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):
|
||||
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)
|
||||
PyCoreNetIf.__init__(self, node=node, name=name, mtu=mtu)
|
||||
self.localname = localname
|
||||
self.up = False
|
||||
self.hwaddr = hwaddr
|
||||
|
@ -76,8 +69,8 @@ class XenVEth(PyCoreNetIf):
|
|||
'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"])
|
||||
subprocess.check_call(cmd)
|
||||
subprocess.check_call([constants.IP_BIN, "link", "set", self.localname, "up"])
|
||||
self.up = True
|
||||
|
||||
def shutdown(self):
|
||||
|
@ -87,28 +80,28 @@ class XenVEth(PyCoreNetIf):
|
|||
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,
|
||||
# check_call([XM_PATH, 'network-detach', self.node.vmname,
|
||||
# self.hwaddr])
|
||||
self.up = False
|
||||
|
||||
|
||||
class XenNode(PyCoreNode):
|
||||
apitype = coreapi.CORE_NODE_XEN
|
||||
apitype = NodeTypes.XEN.value
|
||||
|
||||
FilesToIgnore = frozenset([
|
||||
#'ipforward.sh',
|
||||
# 'ipforward.sh',
|
||||
'quaggaboot.sh',
|
||||
])
|
||||
|
||||
FilesRedirection = {
|
||||
'ipforward.sh' : '/core-tmp/ipforward.sh',
|
||||
'ipforward.sh': '/core-tmp/ipforward.sh',
|
||||
}
|
||||
|
||||
CmdsToIgnore = frozenset([
|
||||
#'sh ipforward.sh',
|
||||
#'sh quaggaboot.sh zebra',
|
||||
#'sh quaggaboot.sh ospfd',
|
||||
#'sh quaggaboot.sh ospf6d',
|
||||
# 'sh ipforward.sh',
|
||||
# 'sh quaggaboot.sh zebra',
|
||||
# 'sh quaggaboot.sh ospfd',
|
||||
# 'sh quaggaboot.sh ospf6d',
|
||||
'killall zebra',
|
||||
'killall ospfd',
|
||||
'killall ospf6d',
|
||||
|
@ -116,43 +109,39 @@ class XenNode(PyCoreNode):
|
|||
])
|
||||
|
||||
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)
|
||||
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')])
|
||||
subprocess.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')])
|
||||
subprocess.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')])
|
||||
subprocess.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,
|
||||
'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):
|
||||
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
|
||||
PyCoreNode.__init__(self, session = session, objid = objid, name = name,
|
||||
verbose = verbose)
|
||||
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
|
||||
|
@ -180,36 +169,35 @@ class XenNode(PyCoreNode):
|
|||
# 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')
|
||||
'quagga/Quagga.conf')
|
||||
|
||||
# LxcNode initialization
|
||||
# self.makenodedir()
|
||||
if self.nodedir is None:
|
||||
self.nodedir = \
|
||||
os.path.join(session.sessiondir, self.name + ".conf")
|
||||
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"
|
||||
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)
|
||||
"""
|
||||
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)
|
||||
logger.warn("XEN PVM startup() called: preparing disk for %s" % self.name)
|
||||
self.lock.acquire()
|
||||
try:
|
||||
if self.up:
|
||||
|
@ -217,10 +205,10 @@ class XenNode(PyCoreNode):
|
|||
self.createlogicalvolume()
|
||||
self.createpartitions()
|
||||
persistdev = self.createfilesystems()
|
||||
check_call([MOUNT_BIN, '-t', 'ext4', persistdev, self.mountdir])
|
||||
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.setrootpassword(pw=self.getconfigitem('root_password'))
|
||||
self.sethostname(old='UBASE', new=self.name)
|
||||
self.setupssh(keypath=self.getconfigitem('ssh_key_path'))
|
||||
self.createvm()
|
||||
|
@ -230,11 +218,11 @@ class XenNode(PyCoreNode):
|
|||
|
||||
# from class LxcNode (also SimpleLxcNode)
|
||||
def boot(self):
|
||||
self.warn("XEN PVM boot() called")
|
||||
logger.warn("XEN PVM boot() called")
|
||||
|
||||
self.lock.acquire()
|
||||
if not self.up:
|
||||
raise Exception, "Can't boot VM without initialized disk"
|
||||
raise Exception("Can't boot VM without initialized disk")
|
||||
|
||||
if self.booted:
|
||||
self.lock.release()
|
||||
|
@ -246,18 +234,17 @@ class XenNode(PyCoreNode):
|
|||
self.untarpersistent(tarname=tarname, iso=False)
|
||||
|
||||
try:
|
||||
check_call([UMOUNT_BIN, self.mountdir])
|
||||
subprocess.check_call([constants.UMOUNT_BIN, self.mountdir])
|
||||
self.unmount_all(self.mountdir)
|
||||
check_call([UDEVADM_PATH, 'settle'])
|
||||
check_call([KPARTX_PATH, '-d', self.lvpath])
|
||||
subprocess.check_call([UDEVADM_PATH, 'settle'])
|
||||
subprocess.check_call([KPARTX_PATH, '-d', self.lvpath])
|
||||
|
||||
#time.sleep(5)
|
||||
#time.sleep(1)
|
||||
# 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])
|
||||
logger.warn("XEN PVM boot() unpause domU %s" % self.vmname)
|
||||
utils.mutecheck_call([XM_PATH, 'unpause', self.vmname])
|
||||
|
||||
self.booted = True
|
||||
finally:
|
||||
|
@ -265,10 +252,10 @@ class XenNode(PyCoreNode):
|
|||
|
||||
def validate(self):
|
||||
self.session.services.validatenodeservices(self)
|
||||
|
||||
|
||||
# from class LxcNode (also SimpleLxcNode)
|
||||
def shutdown(self):
|
||||
self.warn("XEN PVM shutdown() called")
|
||||
logger.warn("XEN PVM shutdown() called")
|
||||
if not self.up:
|
||||
return
|
||||
self.lock.acquire()
|
||||
|
@ -281,27 +268,25 @@ class XenNode(PyCoreNode):
|
|||
try:
|
||||
# RJE XXX what to do here
|
||||
if self.booted:
|
||||
mutecheck_call([XM_PATH, 'destroy', self.vmname])
|
||||
utils.mutecheck_call([XM_PATH, 'destroy', self.vmname])
|
||||
self.booted = False
|
||||
except OSError:
|
||||
pass
|
||||
except subprocess.CalledProcessError:
|
||||
except (OSError, subprocess.CalledProcessError):
|
||||
# ignore this error too, the VM may have exited already
|
||||
pass
|
||||
logger.exception("error during shutdown")
|
||||
|
||||
# discard LVM volume
|
||||
lvmRemoveCount = 0
|
||||
while os.path.exists(self.lvpath):
|
||||
try:
|
||||
check_call([UDEVADM_PATH, 'settle'])
|
||||
mutecall([LVCHANGE_PATH, '-an', self.lvpath])
|
||||
subprocess.check_call([UDEVADM_PATH, 'settle'])
|
||||
utils.mutecall([LVCHANGE_PATH, '-an', self.lvpath])
|
||||
lvmRemoveCount += 1
|
||||
mutecall([LVREMOVE_PATH, '-f', self.lvpath])
|
||||
utils.mutecall([LVREMOVE_PATH, '-f', self.lvpath])
|
||||
except OSError:
|
||||
pass
|
||||
if (lvmRemoveCount > 1):
|
||||
self.warn("XEN PVM shutdown() required %d lvremove " \
|
||||
"executions." % lvmRemoveCount)
|
||||
logger.exception("error during shutdown")
|
||||
|
||||
if lvmRemoveCount > 1:
|
||||
logger.warn("XEN PVM shutdown() required %d lvremove executions." % lvmRemoveCount)
|
||||
|
||||
self._netif.clear()
|
||||
del self.session
|
||||
|
@ -313,117 +298,124 @@ class XenNode(PyCoreNode):
|
|||
self.lock.release()
|
||||
|
||||
def createlogicalvolume(self):
|
||||
''' Create a logical volume for this Xen domU. Called from startup().
|
||||
'''
|
||||
"""
|
||||
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])
|
||||
utils.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.
|
||||
'''
|
||||
"""
|
||||
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);
|
||||
persist_size = int(0.75 * constraint.maxSize)
|
||||
self.createpartition(device=dev, disk=disk, start=1,
|
||||
end=(persist_size - 1) , type="ext4")
|
||||
end=persist_size - 1, type="ext4")
|
||||
self.createpartition(device=dev, disk=disk, start=persist_size,
|
||||
end=(constraint.maxSize - 1) , type="linux-swap(v1)")
|
||||
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.
|
||||
'''
|
||||
"""
|
||||
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)
|
||||
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.
|
||||
'''
|
||||
"""
|
||||
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])
|
||||
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):
|
||||
''' 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
|
||||
"""
|
||||
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))
|
||||
except IOError:
|
||||
logger.exception("Failed to open ISO file: %s", self.isofile)
|
||||
return
|
||||
try:
|
||||
tardata = fs.open_file(tarname).read();
|
||||
except IOError, e:
|
||||
self.warn("Failed to open tar file: %s (%s)" % (tarname, e))
|
||||
tardata = fs.open_file(tarname).read()
|
||||
except IOError:
|
||||
logger.exception("Failed to open tar file: %s", tarname)
|
||||
return
|
||||
finally:
|
||||
del fs;
|
||||
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))
|
||||
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)
|
||||
'-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')])
|
||||
"""
|
||||
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):
|
||||
''' 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')])
|
||||
"""
|
||||
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):
|
||||
''' Configure SSH access by installing host keys and a system-wide
|
||||
authorized_keys file.
|
||||
'''
|
||||
"""
|
||||
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])
|
||||
subprocess.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'):
|
||||
sshdir = sshdir.replace('/', '\\/') # backslash slashes for use in sed
|
||||
subprocess.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)
|
||||
|
@ -431,10 +423,11 @@ class XenNode(PyCoreNode):
|
|||
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.
|
||||
'''
|
||||
"""
|
||||
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')
|
||||
|
@ -445,110 +438,109 @@ class XenNode(PyCoreNode):
|
|||
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)
|
||||
utils.mutecheck_call(args)
|
||||
|
||||
# from class LxcNode
|
||||
def privatedir(self, path):
|
||||
#self.warn("XEN PVM privatedir() called")
|
||||
# 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()"
|
||||
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
|
||||
def openpausednodefile(self, filename, mode = "w"):
|
||||
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 = dirname.replace("/", ".")
|
||||
dirname = os.path.join(self.nodedir, dirname)
|
||||
if not os.path.isdir(dirname):
|
||||
os.makedirs(dirname, mode = 0755)
|
||||
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):
|
||||
def nodefile(self, filename, contents, mode=0644):
|
||||
if filename in self.FilesToIgnore:
|
||||
#self.warn("XEN PVM nodefile(filename=%s) ignored" % [filename])
|
||||
# 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))
|
||||
logger.warn("XEN PVM nodefile(filename=%s) redirected to %s" % (filename, redirFilename))
|
||||
filename = redirFilename
|
||||
|
||||
self.warn("XEN PVM nodefile(filename=%s) called" % [filename])
|
||||
|
||||
logger.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
|
||||
raise Exception("Can't access VM file as VM disk isn't ready")
|
||||
|
||||
if self.booted:
|
||||
self.lock.release()
|
||||
raise Exception, "Can't access VM file as VM is already running"
|
||||
return
|
||||
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()
|
||||
self.info("created nodefile: '%s'; mode: 0%o" % (f.name, mode))
|
||||
logger.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
|
||||
return False # XXX
|
||||
|
||||
def cmd(self, args, wait = True):
|
||||
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)
|
||||
# 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
|
||||
logger("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, "")
|
||||
# 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" % cmdAsString)
|
||||
return 0, ""
|
||||
|
||||
def popen(self, args):
|
||||
cmdAsString = string.join(args, ' ')
|
||||
self.warn("XEN PVM popen(args=[%s]) called, but not yet implemented" % cmdAsString)
|
||||
logger.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)
|
||||
logger.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")
|
||||
def term(self, sh="/bin/sh"):
|
||||
logger.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.
|
||||
'''
|
||||
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:
|
||||
|
@ -560,33 +552,22 @@ class XenNode(PyCoreNode):
|
|||
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
|
||||
# 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
|
||||
|
||||
def shcmd(self, cmdstr, sh = "/bin/sh"):
|
||||
self.warn("XEN PVM shcmd(args=[%s]) called, but not yet implemented" % cmdstr)
|
||||
def shcmd(self, cmdstr, sh="/bin/sh"):
|
||||
logger("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")
|
||||
logger.warn("XEN PVM Nodes can't bind-mount filesystems")
|
||||
|
||||
def umount(self, target):
|
||||
self.warn("XEN PVM Nodes can't bind-mount filesystems")
|
||||
logger.warn("XEN PVM Nodes can't bind-mount filesystems")
|
||||
|
||||
def newifindex(self):
|
||||
self.lock.acquire()
|
||||
|
@ -606,16 +587,16 @@ class XenNode(PyCoreNode):
|
|||
return -1
|
||||
|
||||
def addnetif(self, netif, ifindex):
|
||||
self.warn("XEN PVM addnetif() called")
|
||||
logger.warn("XEN PVM addnetif() called")
|
||||
PyCoreNode.addnetif(self, netif, ifindex)
|
||||
|
||||
def delnetif(self, ifindex):
|
||||
self.warn("XEN PVM delnetif() called")
|
||||
logger.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))
|
||||
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:
|
||||
|
@ -623,12 +604,12 @@ class XenNode(PyCoreNode):
|
|||
ifindex = self.newifindex()
|
||||
if ifname is None:
|
||||
ifname = "eth%d" % ifindex
|
||||
sessionid = self.session.shortsessionid()
|
||||
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
|
||||
veth = ifclass(node = self, name = name, localname = localname,
|
||||
mtu = 1500, net = net, hwaddr = hwaddr)
|
||||
veth = ifclass(node=self, name=name, localname=localname,
|
||||
mtu=1500, net=net, hwaddr=hwaddr)
|
||||
|
||||
veth.name = ifname
|
||||
try:
|
||||
|
@ -641,14 +622,14 @@ class XenNode(PyCoreNode):
|
|||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def newtuntap(self, ifindex = None, ifname = None, net = None):
|
||||
self.warn("XEN PVM newtuntap() called but not implemented")
|
||||
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
|
||||
#self.cmd([IP_BIN, "link", "set", "dev", self.ifname(ifindex),
|
||||
# self.cmd([IP_BIN, "link", "set", "dev", self.ifname(ifindex),
|
||||
# "address", str(addr)])
|
||||
|
||||
def addaddr(self, ifindex, addr):
|
||||
|
@ -662,49 +643,49 @@ class XenNode(PyCoreNode):
|
|||
try:
|
||||
self._netif[ifindex].deladdr(addr)
|
||||
except ValueError:
|
||||
self.warn("trying to delete unknown address: %s" % addr)
|
||||
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")
|
||||
def delalladdr(self, ifindex, addrtypes = valid_deladdrtype):
|
||||
addr = self.getaddr(self.ifname(ifindex), rescan = True)
|
||||
|
||||
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)
|
||||
" ".join(self.valid_deladdrtype)
|
||||
for a in addr[t]:
|
||||
self.deladdr(ifindex, a)
|
||||
# update cached information
|
||||
self.getaddr(self.ifname(ifindex), rescan = True)
|
||||
self.getaddr(self.ifname(ifindex), rescan=True)
|
||||
|
||||
# Xen PVM relies on boot process to bring up links
|
||||
#def ifup(self, ifindex):
|
||||
# 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))
|
||||
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()
|
||||
raise Exception, "Can't access add veth as VM disk isn't ready"
|
||||
return
|
||||
raise Exception("Can't access add veth as VM disk isn't ready")
|
||||
|
||||
if self.booted:
|
||||
self.lock.release()
|
||||
raise Exception, "Can't access add veth as VM is already running"
|
||||
return
|
||||
raise Exception("Can't access add veth as VM is already running")
|
||||
|
||||
try:
|
||||
if isinstance(net, EmaneNode):
|
||||
raise Exception, "Xen PVM doesn't yet support Emane nets"
|
||||
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)
|
||||
|
@ -719,8 +700,8 @@ class XenNode(PyCoreNode):
|
|||
# netif.addaddr(addr)
|
||||
# return ifindex
|
||||
else:
|
||||
ifindex = self.newveth(ifindex = ifindex, ifname = ifname,
|
||||
net = net, hwaddr = hwaddr)
|
||||
ifindex = self.newveth(ifindex=ifindex, ifname=ifname,
|
||||
net=net, hwaddr=hwaddr)
|
||||
if net is not None:
|
||||
self.attachnet(ifindex, net)
|
||||
|
||||
|
@ -728,24 +709,27 @@ class XenNode(PyCoreNode):
|
|||
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))
|
||||
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.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):
|
||||
for addr in utils.maketuple(addrlist):
|
||||
self.addaddr(ifindex, addr)
|
||||
#self.ifup(ifindex)
|
||||
# self.ifup(ifindex)
|
||||
return ifindex
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def connectnode(self, ifname, othernode, otherifname):
|
||||
self.warn("XEN PVM connectnode() called")
|
||||
logger.warn("XEN PVM connectnode() called")
|
||||
|
||||
# tmplen = 8
|
||||
# tmp1 = "tmp." + "".join([random.choice(string.ascii_lowercase)
|
||||
|
@ -768,21 +752,19 @@ class XenNode(PyCoreNode):
|
|||
self.lock.acquire()
|
||||
if not self.up:
|
||||
self.lock.release()
|
||||
raise Exception, "Can't access VM file as VM disk isn't ready"
|
||||
return
|
||||
raise Exception("Can't access VM file as VM disk isn't ready")
|
||||
|
||||
if self.booted:
|
||||
self.lock.release()
|
||||
raise Exception, "Can't access VM file as VM is already running"
|
||||
return
|
||||
raise Exception("Can't access VM file as VM is already running")
|
||||
|
||||
if filename in self.FilesToIgnore:
|
||||
#self.warn("XEN PVM addfile(filename=%s) ignored" % [filename])
|
||||
# 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))
|
||||
logger.warn("XEN PVM addfile(filename=%s) redirected to %s" % (filename, redirFilename))
|
||||
filename = redirFilename
|
||||
|
||||
try:
|
||||
|
@ -794,24 +776,24 @@ class XenNode(PyCoreNode):
|
|||
fout.write(contents)
|
||||
os.chmod(fout.name, mode)
|
||||
fout.close()
|
||||
self.info("created nodefile: '%s'; mode: 0%o" % (fout.name, mode))
|
||||
logger.info("created nodefile: '%s'; mode: 0%o" % (fout.name, mode))
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
self.warn("XEN PVM addfile(filename=%s) called" % [filename])
|
||||
logger.warn("XEN PVM addfile(filename=%s) called" % [filename])
|
||||
|
||||
#shcmd = "mkdir -p $(dirname '%s') && mv '%s' '%s' && sync" % \
|
||||
# shcmd = "mkdir -p $(dirname '%s') && mv '%s' '%s' && sync" % \
|
||||
# (filename, srcname, filename)
|
||||
#self.shcmd(shcmd)
|
||||
# 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.
|
||||
'''
|
||||
"""
|
||||
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():
|
||||
for o in self.session.objects.itervalues():
|
||||
if not isinstance(o, LxcNode):
|
||||
continue
|
||||
o.umount(path)
|
||||
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
#
|
||||
# CORE
|
||||
# Copyright (c)2011-2012 the Boeing Company.
|
||||
# See the LICENSE file included in this distribution.
|
||||
#
|
||||
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
|
||||
#
|
||||
'''
|
||||
"""
|
||||
xenconfig.py: Implementation of the XenConfigManager class for managing
|
||||
configurable items for XenNodes.
|
||||
|
||||
|
@ -17,72 +10,93 @@ Node type config: XenConfigManager.configs[0] = (type='mytype', values)
|
|||
All nodes of this type have this config.
|
||||
Node-specific config: XenConfigManager.configs[nodenumber] = (type, values)
|
||||
The node having this specific number has this config.
|
||||
'''
|
||||
"""
|
||||
|
||||
import sys, os, threading, subprocess, time, string
|
||||
import ConfigParser
|
||||
from xml.dom.minidom import parseString, Document
|
||||
from core.constants import *
|
||||
import os
|
||||
import string
|
||||
|
||||
from core import constants
|
||||
from core.api import coreapi
|
||||
from core.conf import ConfigurableManager, Configurable
|
||||
from core.conf import Configurable
|
||||
from core.conf import ConfigurableManager
|
||||
from core.enumerations import ConfigDataTypes
|
||||
from core.enumerations import ConfigFlags
|
||||
from core.enumerations import ConfigTlvs
|
||||
from core.enumerations import RegisterTlvs
|
||||
from core.misc import log
|
||||
|
||||
logger = log.get_logger(__name__)
|
||||
|
||||
|
||||
class XenConfigManager(ConfigurableManager):
|
||||
''' Xen controller object. Lives in a Session instance and is used for
|
||||
building Xen profiles.
|
||||
'''
|
||||
_name = "xen"
|
||||
_type = coreapi.CORE_TLV_REG_EMULSRV
|
||||
|
||||
"""
|
||||
Xen controller object. Lives in a Session instance and is used for
|
||||
building Xen profiles.
|
||||
"""
|
||||
name = "xen"
|
||||
config_type = RegisterTlvs.EMULATION_SERVER.value
|
||||
|
||||
def __init__(self, session):
|
||||
ConfigurableManager.__init__(self, session)
|
||||
self.verbose = self.session.getcfgitembool('verbose', False)
|
||||
self.default_config = XenDefaultConfig(session, objid=None)
|
||||
"""
|
||||
Creates a XenConfigManager instance.
|
||||
|
||||
:param core.session.Session session: session this manager is tied to
|
||||
:return: nothing
|
||||
"""
|
||||
ConfigurableManager.__init__(self)
|
||||
self.default_config = XenDefaultConfig(session, object_id=None)
|
||||
self.loadconfigfile()
|
||||
|
||||
def setconfig(self, nodenum, conftype, values):
|
||||
''' add configuration values for a node to a dictionary; values are
|
||||
usually received from a Configuration Message, and may refer to a
|
||||
node for which no object exists yet
|
||||
'''
|
||||
if nodenum is None:
|
||||
nodenum = 0 # used for storing the global default config
|
||||
"""
|
||||
add configuration values for a node to a dictionary; values are
|
||||
usually received from a Configuration Message, and may refer to a
|
||||
node for which no object exists yet
|
||||
"""
|
||||
if nodenum is None:
|
||||
nodenum = 0 # used for storing the global default config
|
||||
return ConfigurableManager.setconfig(self, nodenum, conftype, values)
|
||||
|
||||
def getconfig(self, nodenum, conftype, defaultvalues):
|
||||
''' get configuration values for a node; if the values don't exist in
|
||||
our dictionary then return the default values supplied; if conftype
|
||||
is None then we return a match on any conftype.
|
||||
'''
|
||||
if nodenum is None:
|
||||
nodenum = 0 # used for storing the global default config
|
||||
return ConfigurableManager.getconfig(self, nodenum, conftype,
|
||||
defaultvalues)
|
||||
"""
|
||||
get configuration values for a node; if the values don't exist in
|
||||
our dictionary then return the default values supplied; if conftype
|
||||
is None then we return a match on any conftype.
|
||||
"""
|
||||
if nodenum is None:
|
||||
nodenum = 0 # used for storing the global default config
|
||||
return ConfigurableManager.getconfig(self, nodenum, conftype, defaultvalues)
|
||||
|
||||
def clearconfig(self, nodenum):
|
||||
''' remove configuration values for a node
|
||||
'''
|
||||
"""
|
||||
remove configuration values for a node
|
||||
"""
|
||||
ConfigurableManager.clearconfig(self, nodenum)
|
||||
if 0 in self.configs:
|
||||
self.configs.pop(0)
|
||||
|
||||
def configure(self, session, msg):
|
||||
''' Handle configuration messages for global Xen config.
|
||||
'''
|
||||
return self.default_config.configure(self, msg)
|
||||
def configure(self, session, config_data):
|
||||
"""
|
||||
Handle configuration messages for global Xen config.
|
||||
|
||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
||||
"""
|
||||
return self.default_config.configure(self, config_data)
|
||||
|
||||
def loadconfigfile(self, filename=None):
|
||||
''' Load defaults from the /etc/core/xen.conf file into dict object.
|
||||
'''
|
||||
"""
|
||||
Load defaults from the /etc/core/xen.conf file into dict object.
|
||||
"""
|
||||
if filename is None:
|
||||
filename = os.path.join(CORE_CONF_DIR, 'xen.conf')
|
||||
filename = os.path.join(constants.CORE_CONF_DIR, 'xen.conf')
|
||||
cfg = ConfigParser.SafeConfigParser()
|
||||
if filename not in cfg.read(filename):
|
||||
self.session.warn("unable to read Xen config file: %s" % filename)
|
||||
logger.warn("unable to read Xen config file: %s" % filename)
|
||||
return
|
||||
section = "xen"
|
||||
if not cfg.has_section(section):
|
||||
self.session.warn("%s is missing a xen section!" % filename)
|
||||
logger.warn("%s is missing a xen section!" % filename)
|
||||
return
|
||||
self.configfile = dict(cfg.items(section))
|
||||
# populate default config items from config file entries
|
||||
|
@ -92,13 +106,14 @@ class XenConfigManager(ConfigurableManager):
|
|||
if names[i] in self.configfile:
|
||||
vals[i] = self.configfile[names[i]]
|
||||
# this sets XenConfigManager.configs[0] = (type='xen', vals)
|
||||
self.setconfig(None, self.default_config._name, vals)
|
||||
self.setconfig(None, self.default_config.name, vals)
|
||||
|
||||
def getconfigitem(self, name, model=None, node=None, value=None):
|
||||
''' Get a config item of the given name, first looking for node-specific
|
||||
configuration, then model specific, and finally global defaults.
|
||||
If a value is supplied, it will override any stored config.
|
||||
'''
|
||||
"""
|
||||
Get a config item of the given name, first looking for node-specific
|
||||
configuration, then model specific, and finally global defaults.
|
||||
If a value is supplied, it will override any stored config.
|
||||
"""
|
||||
if value is not None:
|
||||
return value
|
||||
n = None
|
||||
|
@ -111,8 +126,8 @@ class XenConfigManager(ConfigurableManager):
|
|||
defaultvalues=None)
|
||||
if v is None:
|
||||
# get item from default config for the machine type
|
||||
(t, v) = self.getconfig(nodenum=None,
|
||||
conftype=self.default_config._name,
|
||||
(t, v) = self.getconfig(nodenum=None,
|
||||
conftype=self.default_config.name,
|
||||
defaultvalues=None)
|
||||
|
||||
confignames = self.default_config.getnames()
|
||||
|
@ -124,142 +139,136 @@ class XenConfigManager(ConfigurableManager):
|
|||
if name in self.configfile:
|
||||
return self.configfile[name]
|
||||
else:
|
||||
#self.warn("missing config item '%s'" % name)
|
||||
# logger.warn("missing config item '%s'" % name)
|
||||
return None
|
||||
|
||||
|
||||
class XenConfig(Configurable):
|
||||
''' Manage Xen configuration profiles.
|
||||
'''
|
||||
|
||||
@classmethod
|
||||
def configure(cls, xen, msg):
|
||||
''' Handle configuration messages for setting up a model.
|
||||
Similar to Configurable.configure(), but considers opaque data
|
||||
for indicating node types.
|
||||
'''
|
||||
reply = None
|
||||
nodenum = msg.gettlv(coreapi.CORE_TLV_CONF_NODE)
|
||||
objname = msg.gettlv(coreapi.CORE_TLV_CONF_OBJ)
|
||||
conftype = msg.gettlv(coreapi.CORE_TLV_CONF_TYPE)
|
||||
opaque = msg.gettlv(coreapi.CORE_TLV_CONF_OPAQUE)
|
||||
"""
|
||||
Manage Xen configuration profiles.
|
||||
"""
|
||||
|
||||
nodetype = objname
|
||||
@classmethod
|
||||
def configure(cls, xen, config_data):
|
||||
"""
|
||||
Handle configuration messages for setting up a model.
|
||||
Similar to Configurable.configure(), but considers opaque data
|
||||
for indicating node types.
|
||||
|
||||
:param xen: xen instance to configure
|
||||
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
|
||||
"""
|
||||
reply = None
|
||||
node_id = config_data.node
|
||||
object_name = config_data.object
|
||||
config_type = config_data.type
|
||||
opaque = config_data.opaque
|
||||
values_str = config_data.data_values
|
||||
|
||||
nodetype = object_name
|
||||
if opaque is not None:
|
||||
opaque_items = opaque.split(':')
|
||||
if len(opaque_items) != 2:
|
||||
xen.warn("xen config: invalid opaque data in conf message")
|
||||
logger.warn("xen config: invalid opaque data in conf message")
|
||||
return None
|
||||
nodetype = opaque_items[1]
|
||||
|
||||
if xen.verbose:
|
||||
xen.info("received configure message for %s" % nodetype)
|
||||
if conftype == coreapi.CONF_TYPE_FLAGS_REQUEST:
|
||||
if xen.verbose:
|
||||
xen.info("replying to configure request for %s " % nodetype)
|
||||
logger.info("received configure message for %s", nodetype)
|
||||
if config_type == ConfigFlags.REQUEST.value:
|
||||
logger.info("replying to configure request for %s " % nodetype)
|
||||
# when object name is "all", the reply to this request may be None
|
||||
# if this node has not been configured for this model; otherwise we
|
||||
# reply with the defaults for this model
|
||||
if objname == "all":
|
||||
typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE
|
||||
if object_name == "all":
|
||||
typeflags = ConfigFlags.UPDATE.value
|
||||
else:
|
||||
typeflags = coreapi.CONF_TYPE_FLAGS_NONE
|
||||
values = xen.getconfig(nodenum, nodetype, defaultvalues=None)[1]
|
||||
typeflags = ConfigFlags.NONE.value
|
||||
values = xen.getconfig(node_id, nodetype, defaultvalues=None)[1]
|
||||
if values is None:
|
||||
# get defaults from default "xen" config which includes
|
||||
# settings from both cls._confdefaultvalues and xen.conf
|
||||
# settings from both cls._confdefaultvalues and xen.conf
|
||||
defaults = cls.getdefaultvalues()
|
||||
values = xen.getconfig(nodenum, cls._name, defaults)[1]
|
||||
values = xen.getconfig(node_id, cls.name, defaults)[1]
|
||||
if values is None:
|
||||
return None
|
||||
# reply with config options
|
||||
if nodenum is None:
|
||||
nodenum = 0
|
||||
reply = cls.toconfmsg(0, nodenum, typeflags, nodetype, values)
|
||||
elif conftype == coreapi.CONF_TYPE_FLAGS_RESET:
|
||||
if objname == "all":
|
||||
xen.clearconfig(nodenum)
|
||||
#elif conftype == coreapi.CONF_TYPE_FLAGS_UPDATE:
|
||||
if node_id is None:
|
||||
node_id = 0
|
||||
reply = cls.config_data(0, node_id, typeflags, nodetype, values)
|
||||
elif config_type == ConfigFlags.RESET.value:
|
||||
if object_name == "all":
|
||||
xen.clearconfig(node_id)
|
||||
# elif conftype == coreapi.CONF_TYPE_FLAGS_UPDATE:
|
||||
else:
|
||||
# store the configuration values for later use, when the XenNode
|
||||
# object has been created
|
||||
if objname is None:
|
||||
xen.info("no configuration object for node %s" % nodenum)
|
||||
if object_name is None:
|
||||
logger.info("no configuration object for node %s" % node_id)
|
||||
return None
|
||||
values_str = msg.gettlv(coreapi.CORE_TLV_CONF_VALUES)
|
||||
if values_str is None:
|
||||
# use default or preconfigured values
|
||||
defaults = cls.getdefaultvalues()
|
||||
values = xen.getconfig(nodenum, cls._name, defaults)[1]
|
||||
values = xen.getconfig(node_id, cls.name, defaults)[1]
|
||||
else:
|
||||
# use new values supplied from the conf message
|
||||
values = values_str.split('|')
|
||||
xen.setconfig(nodenum, nodetype, values)
|
||||
xen.setconfig(node_id, nodetype, values)
|
||||
|
||||
return reply
|
||||
|
||||
@classmethod
|
||||
def toconfmsg(cls, flags, nodenum, typeflags, nodetype, values):
|
||||
''' Convert this class to a Config API message. Some TLVs are defined
|
||||
by the class, but node number, conf type flags, and values must
|
||||
be passed in.
|
||||
'''
|
||||
def config_data(cls, flags, node_id, type_flags, nodetype, values):
|
||||
"""
|
||||
Convert this class to a Config API message. Some TLVs are defined
|
||||
by the class, but node number, conf type flags, and values must
|
||||
be passed in.
|
||||
"""
|
||||
values_str = string.join(values, '|')
|
||||
tlvdata = ""
|
||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_NODE, nodenum)
|
||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_OBJ,
|
||||
cls._name)
|
||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_TYPE,
|
||||
typeflags)
|
||||
datatypes = tuple( map(lambda x: x[1], cls._confmatrix) )
|
||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_DATA_TYPES,
|
||||
datatypes)
|
||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_VALUES,
|
||||
values_str)
|
||||
captions = reduce( lambda a,b: a + '|' + b, \
|
||||
map(lambda x: x[4], cls._confmatrix))
|
||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_CAPTIONS,
|
||||
captions)
|
||||
possiblevals = reduce( lambda a,b: a + '|' + b, \
|
||||
map(lambda x: x[3], cls._confmatrix))
|
||||
tlvdata += coreapi.CoreConfTlv.pack(
|
||||
coreapi.CORE_TLV_CONF_POSSIBLE_VALUES, possiblevals)
|
||||
if cls._bitmap is not None:
|
||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_BITMAP,
|
||||
cls._bitmap)
|
||||
if cls._confgroups is not None:
|
||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_GROUPS,
|
||||
cls._confgroups)
|
||||
opaque = "%s:%s" % (cls._name, nodetype)
|
||||
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_OPAQUE,
|
||||
opaque)
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.NODE.value, node_id)
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OBJECT.value, cls.name)
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, type_flags)
|
||||
datatypes = tuple(map(lambda x: x[1], cls.config_matrix))
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.DATA_TYPES.value, datatypes)
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.VALUES.value, values_str)
|
||||
captions = reduce(lambda a, b: a + '|' + b, map(lambda x: x[4], cls.config_matrix))
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.CAPTIONS, captions)
|
||||
possiblevals = reduce(lambda a, b: a + '|' + b, map(lambda x: x[3], cls.config_matrix))
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.POSSIBLE_VALUES.value, possiblevals)
|
||||
if cls.bitmap is not None:
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.BITMAP.value, cls.bitmap)
|
||||
if cls.config_groups is not None:
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.GROUPS.value, cls.config_groups)
|
||||
opaque = "%s:%s" % (cls.name, nodetype)
|
||||
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OPAQUE.value, opaque)
|
||||
msg = coreapi.CoreConfMessage.pack(flags, tlvdata)
|
||||
return msg
|
||||
|
||||
|
||||
class XenDefaultConfig(XenConfig):
|
||||
''' Global default Xen configuration options.
|
||||
'''
|
||||
_name = "xen"
|
||||
"""
|
||||
Global default Xen configuration options.
|
||||
"""
|
||||
name = "xen"
|
||||
# Configuration items:
|
||||
# ('name', 'type', 'default', 'possible-value-list', 'caption')
|
||||
_confmatrix = [
|
||||
('ram_size', coreapi.CONF_DATA_TYPE_STRING, '256', '',
|
||||
config_matrix = [
|
||||
('ram_size', ConfigDataTypes.STRING.value, '256', '',
|
||||
'ram size (MB)'),
|
||||
('disk_size', coreapi.CONF_DATA_TYPE_STRING, '256M', '',
|
||||
('disk_size', ConfigDataTypes.STRING.value, '256M', '',
|
||||
'disk size (use K/M/G suffix)'),
|
||||
('iso_file', coreapi.CONF_DATA_TYPE_STRING, '', '',
|
||||
('iso_file', ConfigDataTypes.STRING.value, '', '',
|
||||
'iso file'),
|
||||
('mount_path', coreapi.CONF_DATA_TYPE_STRING, '', '',
|
||||
('mount_path', ConfigDataTypes.STRING.value, '', '',
|
||||
'mount path'),
|
||||
('etc_path', coreapi.CONF_DATA_TYPE_STRING, '', '',
|
||||
('etc_path', ConfigDataTypes.STRING.value, '', '',
|
||||
'etc path'),
|
||||
('persist_tar_iso', coreapi.CONF_DATA_TYPE_STRING, '', '',
|
||||
('persist_tar_iso', ConfigDataTypes.STRING.value, '', '',
|
||||
'iso persist tar file'),
|
||||
('persist_tar', coreapi.CONF_DATA_TYPE_STRING, '', '',
|
||||
('persist_tar', ConfigDataTypes.STRING.value, '', '',
|
||||
'persist tar file'),
|
||||
('root_password', coreapi.CONF_DATA_TYPE_STRING, 'password', '',
|
||||
('root_password', ConfigDataTypes.STRING.value, 'password', '',
|
||||
'root password'),
|
||||
]
|
||||
|
||||
_confgroups = "domU properties:1-%d" % len(_confmatrix)
|
||||
]
|
||||
|
||||
config_groups = "domU properties:1-%d" % len(config_matrix)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue