summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Southgate <andy.southgate@citrix.com>2011-01-14 16:13:50 +0000
committerAndy Southgate <andy.southgate@citrix.com>2011-01-14 16:13:50 +0000
commitfe22186eb989b0302e1cb26a5b92cd77ce47bb9b (patch)
tree175eadae44ba6d636e54f6dbdd0b6bdc2008fab6
parentef7768154324bbddb56d2c7d7a9d2e354b7d60cf (diff)
OS-55: Inject network settings in linux images
-rw-r--r--nova/virt/conn_common.py50
-rw-r--r--nova/virt/disk.py19
-rw-r--r--nova/virt/libvirt_conn.py24
-rw-r--r--nova/virt/xenapi/vm_utils.py36
-rw-r--r--nova/virt/xenapi/vmops.py7
-rwxr-xr-xplugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py1
6 files changed, 110 insertions, 27 deletions
diff --git a/nova/virt/conn_common.py b/nova/virt/conn_common.py
new file mode 100644
index 000000000..bd9ed7794
--- /dev/null
+++ b/nova/virt/conn_common.py
@@ -0,0 +1,50 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2010 Citrix Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import utils
+
+LOG = logging.getLogger('nova.virt.conn_common')
+FLAGS = flags.FLAGS
+
+flags.DEFINE_string('injected_network_template',
+ utils.abspath('virt/interfaces.template'),
+ 'Template file for injected network')
+
+def get_injectables(inst):
+ key = str(inst['key_data'])
+ net = None
+ network_ref = db.network_get_by_instance(context.get_admin_context(),
+ inst['id'])
+ if network_ref['injected']:
+ admin_context = context.get_admin_context()
+ address = db.instance_get_fixed_address(admin_context, inst['id'])
+ ra_server = network_ref['ra_server']
+ if not ra_server:
+ ra_server = "fd00::"
+ with open(FLAGS.injected_network_template) as f:
+ net = f.read() % {'address': address,
+ 'netmask': network_ref['netmask'],
+ 'gateway': network_ref['gateway'],
+ 'broadcast': network_ref['broadcast'],
+ 'dns': network_ref['dns'],
+ 'ra_server': ra_server}
+
+ return key, net
diff --git a/nova/virt/disk.py b/nova/virt/disk.py
index c5565abfa..88b05f0c0 100644
--- a/nova/virt/disk.py
+++ b/nova/virt/disk.py
@@ -92,11 +92,7 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False):
% err)
try:
- if key:
- # inject key file
- _inject_key_into_fs(key, tmpdir)
- if net:
- _inject_net_into_fs(net, tmpdir)
+ inject_data_into_fs(tmpdir, key, net, execute)
finally:
# unmount device
utils.execute('sudo umount %s' % mapped_device)
@@ -158,8 +154,17 @@ def _allocate_device():
def _free_device(device):
_DEVICES.append(device)
+def inject_data_into_fs(fs, key, net, execute):
+ """Injects data into a filesystem already mounted by the caller.
+ Virt connections can call this directly if they mount their fs
+ in a different way to inject_data
+ """
+ if key:
+ _inject_key_into_fs(key, fs, execute=execute)
+ if net:
+ _inject_net_into_fs(net, fs, execute=execute)
-def _inject_key_into_fs(key, fs):
+def _inject_key_into_fs(key, fs, execute=None):
"""Add the given public ssh key to root's authorized_keys.
key is an ssh key string.
@@ -173,7 +178,7 @@ def _inject_key_into_fs(key, fs):
utils.execute('sudo tee -a %s' % keyfile, '\n' + key.strip() + '\n')
-def _inject_net_into_fs(net, fs):
+def _inject_net_into_fs(net, fs, execute=None):
"""Inject /etc/network/interfaces into the filesystem rooted at fs.
net is the contents of /etc/network/interfaces.
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 4e0fd106f..45d8754ab 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -62,6 +62,7 @@ from nova.compute import instance_types
from nova.compute import power_state
from nova.virt import disk
from nova.virt import images
+from nova.virt import conn_common
libvirt = None
libxml2 = None
@@ -74,9 +75,7 @@ FLAGS = flags.FLAGS
flags.DEFINE_string('rescue_image_id', 'ami-rescue', 'Rescue ami image')
flags.DEFINE_string('rescue_kernel_id', 'aki-rescue', 'Rescue aki image')
flags.DEFINE_string('rescue_ramdisk_id', 'ari-rescue', 'Rescue ari image')
-flags.DEFINE_string('injected_network_template',
- utils.abspath('virt/interfaces.template'),
- 'Template file for injected network')
+
flags.DEFINE_string('libvirt_xml_template',
utils.abspath('virt/libvirt.xml.template'),
'Libvirt XML Template')
@@ -622,23 +621,8 @@ class LibvirtConnection(object):
if not inst['kernel_id']:
target_partition = "1"
- key = str(inst['key_data'])
- net = None
- network_ref = db.network_get_by_instance(context.get_admin_context(),
- inst['id'])
- if network_ref['injected']:
- admin_context = context.get_admin_context()
- address = db.instance_get_fixed_address(admin_context, inst['id'])
- ra_server = network_ref['ra_server']
- if not ra_server:
- ra_server = "fd00::"
- with open(FLAGS.injected_network_template) as f:
- net = f.read() % {'address': address,
- 'netmask': network_ref['netmask'],
- 'gateway': network_ref['gateway'],
- 'broadcast': network_ref['broadcast'],
- 'dns': network_ref['dns'],
- 'ra_server': ra_server}
+ key, net = conn_common.get_injectables(inst)
+
if key or net:
inst_name = inst['name']
img_id = inst.image_id
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 4bbd522c1..70f81b3b6 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -22,6 +22,7 @@ their attributes like VDIs, VIFs, as well as their lookup functions.
import os
import pickle
import re
+import tempfile
import time
import urllib
from xml.dom import minidom
@@ -33,10 +34,12 @@ from nova import flags
from nova import log as logging
from nova import utils
from nova.auth.manager import AuthManager
+from nova.compute import disk
from nova.compute import instance_types
from nova.compute import power_state
from nova.virt import images
from nova.virt.xenapi import HelperBase
+from nova.virt import conn_common
from nova.virt.xenapi.volume_utils import StorageError
@@ -440,6 +443,39 @@ class VMHelper(HelperBase):
return None
@classmethod
+ def preconfigure_instance(cls, session, instance, vdi_ref):
+ """Makes alterations to the image before launching as part of spawn.
+ May also set xenstore values to modify the image behaviour after
+ VM start."""
+
+ # As mounting the image VDI is expensive, we only want do do it once,
+ # if at all, so determine whether it's required first, and then do
+ # everything
+ mount_required = False
+ key, net = conn_common.get_injectables(instance)
+ if key is not None or net is not None:
+ mount_required = True
+
+ if mount_required:
+ def _mounted_processing(device):
+ devPath = '/dev/'+device+'1' # Note: 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)
+ try:
+ 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 compile_info(cls, record):
"""Fill record with VM status information"""
LOG.info(_("(VM_UTILS) xenserver vm state -> |%s|"),
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index e84ce20c4..efab9c9ce 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -79,6 +79,9 @@ class VMOps(object):
disk_image_type = ImageType.DISK
else:
disk_image_type = ImageType.DISK_RAW
+ # TODO: Coalesce fetch_image, lookup_image and
+ # manipulate_root_image so requires a single VDI mount/umount
+ # sequence
vdi_uuid = VMHelper.fetch_image(self._session, instance.id,
instance.image_id, user, project, disk_image_type)
vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid)
@@ -102,6 +105,10 @@ class VMOps(object):
if network_ref:
VMHelper.create_vif(self._session, vm_ref,
network_ref, instance.mac_address)
+
+ # Alter the image before VM start for, e.g. network injection
+ VMHelper.preconfigure_instance(self._session, instance, vdi_ref)
+
LOG.debug(_('Starting VM %s...'), vm_ref)
self._session.call_xenapi('VM.start', vm_ref, False, False)
instance_name = instance.name
diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py
index f51f5fce4..dbbce5c51 100755
--- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py
+++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py
@@ -28,6 +28,7 @@ import re
import time
import XenAPI
+import XenAPI
##### Logging setup