summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorAndy Southgate <andy.southgate@citrix.com>2011-01-20 16:49:54 +0000
committerAndy Southgate <andy.southgate@citrix.com>2011-01-20 16:49:54 +0000
commit88be6540d2a796e313f2d8ef4ccc6e66ba1a3ed1 (patch)
treedf0f1793e310878168adcf56a71a26a49209c258 /nova
parentfe22186eb989b0302e1cb26a5b92cd77ce47bb9b (diff)
OS-55: Only modify Linux image with no or injection-incapable guest agent
OS-55: Support network configuration via xenstore for Windows images
Diffstat (limited to 'nova')
-rw-r--r--nova/virt/xenapi/vm_utils.py105
-rw-r--r--nova/virt/xenapi/vmops.py3
2 files changed, 99 insertions, 9 deletions
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 70f81b3b6..79d529ce2 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -29,6 +29,8 @@ from xml.dom import minidom
from eventlet import event
import glance.client
+from nova import context
+from nova import db
from nova import exception
from nova import flags
from nova import log as logging
@@ -458,22 +460,107 @@ class VMHelper(HelperBase):
if mount_required:
def _mounted_processing(device):
- devPath = '/dev/'+device+'1' # Note: Partition 1 hardcoded
+ devPath = '/dev/'+device+'1' # NB: Partition 1 hardcoded
tmpdir = tempfile.mkdtemp()
try:
- out, err = utils.execute('sudo mount %s %s' % (devPath, tmpdir))
- if err:
- raise exception.Error(_('Failed to mount filesystem: %s') % err)
+ # Mount only Linux filesystems, as we mustn't disturb NTFS images
try:
- disk.inject_data_into_fs(tmpdir, key, net, utils.execute)
- finally:
- utils.execute('sudo umount %s' % devPath)
+ out, err = utils.execute('sudo mount -t ext2,ext3 "%s" "%s"' % (devPath, tmpdir))
+ except exception.ProcessExecutionError as e:
+ err = str(e)
+ if err:
+ LOG.info('Failed to mount filesystem (expected for non-linux instances): %s' % err)
+ else:
+ try:
+ # This try block ensures that the umount occurs
+
+ xe_update_networking_filename = os.path.join(tmpdir, 'usr', 'sbin', 'xe-update-networking')
+ if os.path.isfile(xe_update_networking_filename):
+ # The presence of the xe-update-networking file indicates that this guest
+ # agent can reconfigure the netwokr from xenstore data, so manipulation
+ # of files in /etc is not required
+ LOG.info('XenServer tools installed in this image are capable of network injection. '
+ 'Networking files will not be manipulated')
+ else:
+ xe_daemon_filename = os.path.join(tmpdir, 'usr', 'sbin', 'xe-daemon')
+ if os.path.isfile(xe_daemon_filename):
+ LOG.info('XenServer tools are present in this image but '
+ 'are not capable of network injection')
+ else:
+ LOG.info('XenServer tools are not installed in this image')
+ LOG.info('Manipulating interface files directly')
+ disk.inject_data_into_fs(tmpdir, key, net, utils.execute)
+ finally:
+ utils.execute('sudo umount "%s"' % devPath)
finally:
# remove temporary directory
os.rmdir(tmpdir)
-
- # FIXME: Check self._session is the type of session this fn wants
+
with_vdi_attached_here(session, vdi_ref, False, _mounted_processing)
+
+
+ @classmethod
+ def preconfigure_xenstore(cls, session, instance, vm_ref):
+ XENSTORE_TYPES = {
+ 'BroadcastAddress' : 'multi_sz',
+ 'DefaultGateway' : 'multi_sz',
+ 'EnableDhcp' : 'dword',
+ 'IPAddress' : 'multi_sz',
+ 'NameServer' : 'string',
+ 'SubnetMask' : 'multi_sz'
+ }
+
+ # Network setup
+ network_ref = db.network_get_by_instance(context.get_admin_context(),
+ instance['id'])
+ if network_ref['injected']:
+ admin_context = context.get_admin_context()
+ address = db.instance_get_fixed_address(admin_context, instance['id'])
+
+ xenstore_data = {
+ # NB: Setting broadcast address is not supported by
+ # Windows or the Windows guest agent, and will be ignored
+ # on that platform
+ 'BroadcastAddress': network_ref['broadcast'],
+ 'EnableDhcp': '0',
+ 'IPAddress': address,
+ 'SubnetMask': network_ref['netmask'],
+ 'DefaultGateway': network_ref['gateway'],
+ 'NameServer': network_ref['dns']
+ }
+
+ device_to_configure = 0 # Configure network device 0 in the VM
+
+ vif_refs = session.call_xenapi('VM.get_VIFs', vm_ref)
+ mac_addr = None
+
+ for vif_ref in vif_refs:
+ device = session.call_xenapi('VIF.get_device', vif_ref)
+ if str(device) == str(device_to_configure):
+ mac_addr = session.call_xenapi('VIF.get_MAC', vif_ref)
+ break
+
+ if mac_addr is None:
+ raise exception.NotFound('Networking device %s not found in VM')
+
+ # MAC address must be upper case in the xenstore key,
+ # with colons replaced by underscores
+ underscore_mac_addr = mac_addr.replace(':', '_')
+ xenstore_prefix='vm-data/vif/'+underscore_mac_addr.upper()+'/tcpip/'
+
+ for xenstore_key, xenstore_value in xenstore_data.iteritems():
+ # NB: The xenstore_key part of the instance_key isn't used but must
+ # be unique. We set it to xenstore_key as a convenient unique name.
+ # The xenstore_key value takes effect in the /name element.
+ instance_key = xenstore_prefix + xenstore_key
+ type = XENSTORE_TYPES[xenstore_key]
+
+ session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/name', xenstore_key)
+ session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/type', type)
+ if type == 'multi_sz':
+ session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/data/0', xenstore_value)
+ else:
+ session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/data', xenstore_value)
@classmethod
def compile_info(cls, record):
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index efab9c9ce..ae477f11e 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -109,6 +109,9 @@ class VMOps(object):
# Alter the image before VM start for, e.g. network injection
VMHelper.preconfigure_instance(self._session, instance, vdi_ref)
+ # Configure the VM's xenstore data before start for, e.g. network configuration
+ VMHelper.preconfigure_xenstore(self._session, instance, vm_ref)
+
LOG.debug(_('Starting VM %s...'), vm_ref)
self._session.call_xenapi('VM.start', vm_ref, False, False)
instance_name = instance.name