Convert file injection code to use the VFS APIs
Remove the requirement to mount disk images on the host filesystem for file injection, by switching over to use the new VFS APIs. The mount code is now solely used for setting up LXC disk images blueprint: virt-disk-api-refactoring Change-Id: I1335bf7a8266d3d1d410a66538969f1213766cb9 Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
This commit is contained in:
parent
74e38f1bae
commit
72918415f2
|
@ -149,49 +149,3 @@ class TestVirtDisk(test.TestCase):
|
|||
self.executes.pop()
|
||||
|
||||
self.assertEqual(self.executes, expected_commands)
|
||||
|
||||
|
||||
class TestVirtDiskPaths(test.TestCase):
|
||||
def setUp(self):
|
||||
super(TestVirtDiskPaths, self).setUp()
|
||||
|
||||
real_execute = utils.execute
|
||||
|
||||
def nonroot_execute(*cmd_parts, **kwargs):
|
||||
kwargs.pop('run_as_root', None)
|
||||
return real_execute(*cmd_parts, **kwargs)
|
||||
|
||||
self.stubs.Set(utils, 'execute', nonroot_execute)
|
||||
|
||||
def test_check_safe_path(self):
|
||||
if tests.utils.is_osx():
|
||||
self.skipTest("Unable to test on OSX")
|
||||
ret = disk_api._join_and_check_path_within_fs('/foo', 'etc',
|
||||
'something.conf')
|
||||
self.assertEquals(ret, '/foo/etc/something.conf')
|
||||
|
||||
def test_check_unsafe_path(self):
|
||||
if tests.utils.is_osx():
|
||||
self.skipTest("Unable to test on OSX")
|
||||
self.assertRaises(exception.Invalid,
|
||||
disk_api._join_and_check_path_within_fs,
|
||||
'/foo', 'etc/../../../something.conf')
|
||||
|
||||
def test_inject_files_with_bad_path(self):
|
||||
if tests.utils.is_osx():
|
||||
self.skipTest("Unable to test on OSX")
|
||||
self.assertRaises(exception.Invalid,
|
||||
disk_api._inject_file_into_fs,
|
||||
'/tmp', '/etc/../../../../etc/passwd',
|
||||
'hax')
|
||||
|
||||
def test_inject_metadata(self):
|
||||
if tests.utils.is_osx():
|
||||
self.skipTest("Unable to test on OSX")
|
||||
with utils.tempdir() as tmpdir:
|
||||
meta_objs = [{"key": "foo", "value": "bar"}]
|
||||
metadata = {"foo": "bar"}
|
||||
disk_api._inject_metadata_into_fs(meta_objs, tmpdir)
|
||||
json_file = os.path.join(tmpdir, 'meta.js')
|
||||
json_data = jsonutils.loads(open(json_file).read())
|
||||
self.assertEqual(metadata, json_data)
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright (C) 2012 Red Hat, 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.
|
||||
|
||||
import sys
|
||||
|
||||
from nova import test
|
||||
|
||||
from nova.tests import fakeguestfs
|
||||
from nova.virt.disk import api as diskapi
|
||||
from nova.virt.disk.vfs import api as vfsapi
|
||||
from nova.virt.disk.vfs import guestfs as vfsguestfs
|
||||
|
||||
|
||||
class VirtDiskTest(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(VirtDiskTest, self).setUp()
|
||||
sys.modules['guestfs'] = fakeguestfs
|
||||
vfsguestfs.guestfs = fakeguestfs
|
||||
|
||||
def test_inject_data_key(self):
|
||||
|
||||
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
|
||||
vfs.setup()
|
||||
|
||||
diskapi._inject_key_into_fs("mysshkey", vfs)
|
||||
|
||||
self.assertTrue("/root/.ssh" in vfs.handle.files)
|
||||
self.assertEquals(vfs.handle.files["/root/.ssh"],
|
||||
{'isdir': True, 'gid': 0, 'uid': 0, 'mode': 0700})
|
||||
self.assertTrue("/root/.ssh/authorized_keys" in vfs.handle.files)
|
||||
self.assertEquals(vfs.handle.files["/root/.ssh/authorized_keys"],
|
||||
{'isdir': False,
|
||||
'content': "Hello World\n# The following ssh " +
|
||||
"key was injected by Nova\nmysshkey\n",
|
||||
'gid': 100,
|
||||
'uid': 100,
|
||||
'mode': 0700})
|
||||
|
||||
vfs.teardown()
|
||||
|
||||
def test_inject_data_key_with_selinux(self):
|
||||
|
||||
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
|
||||
vfs.setup()
|
||||
|
||||
vfs.make_path("etc/selinux")
|
||||
vfs.make_path("etc/rc.d")
|
||||
diskapi._inject_key_into_fs("mysshkey", vfs)
|
||||
|
||||
self.assertTrue("/etc/rc.d/rc.local" in vfs.handle.files)
|
||||
self.assertEquals(vfs.handle.files["/etc/rc.d/rc.local"],
|
||||
{'isdir': False,
|
||||
'content': "Hello World#!/bin/sh\n# Added by " +
|
||||
"Nova to ensure injected ssh keys " +
|
||||
"have the right context\nrestorecon " +
|
||||
"-RF root/.ssh 2>/dev/null || :\n",
|
||||
'gid': 100,
|
||||
'uid': 100,
|
||||
'mode': 0700})
|
||||
|
||||
self.assertTrue("/root/.ssh" in vfs.handle.files)
|
||||
self.assertEquals(vfs.handle.files["/root/.ssh"],
|
||||
{'isdir': True, 'gid': 0, 'uid': 0, 'mode': 0700})
|
||||
self.assertTrue("/root/.ssh/authorized_keys" in vfs.handle.files)
|
||||
self.assertEquals(vfs.handle.files["/root/.ssh/authorized_keys"],
|
||||
{'isdir': False,
|
||||
'content': "Hello World\n# The following ssh " +
|
||||
"key was injected by Nova\nmysshkey\n",
|
||||
'gid': 100,
|
||||
'uid': 100,
|
||||
'mode': 0700})
|
||||
|
||||
vfs.teardown()
|
||||
|
||||
def test_inject_net(self):
|
||||
|
||||
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
|
||||
vfs.setup()
|
||||
|
||||
diskapi._inject_net_into_fs("mynetconfig", vfs)
|
||||
|
||||
self.assertTrue("/etc/network/interfaces" in vfs.handle.files)
|
||||
self.assertEquals(vfs.handle.files["/etc/network/interfaces"],
|
||||
{'content': 'mynetconfig',
|
||||
'gid': 100,
|
||||
'isdir': False,
|
||||
'mode': 0700,
|
||||
'uid': 100})
|
||||
vfs.teardown()
|
||||
|
||||
def test_inject_metadata(self):
|
||||
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
|
||||
vfs.setup()
|
||||
|
||||
diskapi._inject_metadata_into_fs([{"key": "foo",
|
||||
"value": "bar"},
|
||||
{"key": "eek",
|
||||
"value": "wizz"}], vfs)
|
||||
|
||||
self.assertTrue("/meta.js" in vfs.handle.files)
|
||||
self.assertEquals(vfs.handle.files["/meta.js"],
|
||||
{'content': '{"foo": "bar", ' +
|
||||
'"eek": "wizz"}',
|
||||
'gid': 100,
|
||||
'isdir': False,
|
||||
'mode': 0700,
|
||||
'uid': 100})
|
||||
vfs.teardown()
|
||||
|
||||
def test_inject_admin_password(self):
|
||||
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
|
||||
vfs.setup()
|
||||
|
||||
def fake_salt():
|
||||
return "1234567890abcdef"
|
||||
|
||||
self.stubs.Set(diskapi, '_generate_salt', fake_salt)
|
||||
|
||||
vfs.handle.write("/etc/shadow",
|
||||
"root:$1$12345678$xxxxx:14917:0:99999:7:::\n" +
|
||||
"bin:*:14495:0:99999:7:::\n" +
|
||||
"daemon:*:14495:0:99999:7:::\n")
|
||||
|
||||
vfs.handle.write("/etc/passwd",
|
||||
"root:x:0:0:root:/root:/bin/bash\n" +
|
||||
"bin:x:1:1:bin:/bin:/sbin/nologin\n" +
|
||||
"daemon:x:2:2:daemon:/sbin:/sbin/nologin\n")
|
||||
|
||||
diskapi._inject_admin_password_into_fs("123456", vfs)
|
||||
|
||||
self.assertEquals(vfs.handle.files["/etc/passwd"],
|
||||
{'content': "root:x:0:0:root:/root:/bin/bash\n" +
|
||||
"bin:x:1:1:bin:/bin:/sbin/nologin\n" +
|
||||
"daemon:x:2:2:daemon:/sbin:" +
|
||||
"/sbin/nologin\n",
|
||||
'gid': 100,
|
||||
'isdir': False,
|
||||
'mode': 0700,
|
||||
'uid': 100})
|
||||
self.assertEquals(vfs.handle.files["/etc/shadow"],
|
||||
{'content': "root:$1$12345678$a4ge4d5iJ5vw" +
|
||||
"vbFS88TEN0:14917:0:99999:7:::\n" +
|
||||
"bin:*:14495:0:99999:7:::\n" +
|
||||
"daemon:*:14495:0:99999:7:::\n",
|
||||
'gid': 100,
|
||||
'isdir': False,
|
||||
'mode': 0700,
|
||||
'uid': 100})
|
||||
vfs.teardown()
|
|
@ -40,6 +40,7 @@ from nova import utils
|
|||
from nova.virt.disk.mount import guestfs
|
||||
from nova.virt.disk.mount import loop
|
||||
from nova.virt.disk.mount import nbd
|
||||
from nova.virt.disk.vfs import api as vfs
|
||||
from nova.virt import images
|
||||
|
||||
|
||||
|
@ -292,15 +293,20 @@ def inject_data(image,
|
|||
If partition is not specified it mounts the image as a single partition.
|
||||
|
||||
"""
|
||||
img = _DiskImage(image=image, partition=partition, use_cow=use_cow)
|
||||
if img.mount():
|
||||
try:
|
||||
inject_data_into_fs(img.mount_dir,
|
||||
key, net, metadata, admin_password, files)
|
||||
finally:
|
||||
img.umount()
|
||||
else:
|
||||
raise exception.NovaException(img.errors)
|
||||
LOG.debug(_("Inject data image=%(image)s key=%(key)s net=%(net)s "
|
||||
"metadata=%(metadata)s admin_password=ha-ha-not-telling-you "
|
||||
"files=%(files)s partition=%(partition)s use_cow=%(use_cow)s")
|
||||
% locals())
|
||||
fmt = "raw"
|
||||
if use_cow:
|
||||
fmt = "qcow2"
|
||||
fs = vfs.VFS.instance_for_image(image, fmt, partition)
|
||||
fs.setup()
|
||||
try:
|
||||
inject_data_into_fs(fs,
|
||||
key, net, metadata, admin_password, files)
|
||||
finally:
|
||||
fs.teardown()
|
||||
|
||||
|
||||
def setup_container(image, container_dir, use_cow=False):
|
||||
|
@ -349,58 +355,32 @@ def inject_data_into_fs(fs, key, net, metadata, admin_password, files):
|
|||
_inject_file_into_fs(fs, path, contents)
|
||||
|
||||
|
||||
def _join_and_check_path_within_fs(fs, *args):
|
||||
'''os.path.join() with safety check for injected file paths.
|
||||
|
||||
Join the supplied path components and make sure that the
|
||||
resulting path we are injecting into is within the
|
||||
mounted guest fs. Trying to be clever and specifying a
|
||||
path with '..' in it will hit this safeguard.
|
||||
'''
|
||||
absolute_path, _err = utils.execute('readlink', '-nm',
|
||||
os.path.join(fs, *args),
|
||||
run_as_root=True)
|
||||
if not absolute_path.startswith(os.path.realpath(fs) + '/'):
|
||||
raise exception.Invalid(_('injected file path not valid'))
|
||||
return absolute_path
|
||||
|
||||
|
||||
def _inject_file_into_fs(fs, path, contents, append=False):
|
||||
absolute_path = _join_and_check_path_within_fs(fs, path.lstrip('/'))
|
||||
|
||||
parent_dir = os.path.dirname(absolute_path)
|
||||
utils.execute('mkdir', '-p', parent_dir, run_as_root=True)
|
||||
|
||||
args = []
|
||||
LOG.debug(_("Inject file fs=%(fs)s path=%(path)s append=%(append)s") %
|
||||
locals())
|
||||
if append:
|
||||
args.append('-a')
|
||||
args.append(absolute_path)
|
||||
|
||||
kwargs = dict(process_input=contents, run_as_root=True)
|
||||
|
||||
utils.execute('tee', *args, **kwargs)
|
||||
fs.append_file(path, contents)
|
||||
else:
|
||||
fs.replace_file(path, contents)
|
||||
|
||||
|
||||
def _inject_metadata_into_fs(metadata, fs):
|
||||
LOG.debug(_("Inject metadata fs=%(fs)s metadata=%(metadata)s") %
|
||||
locals())
|
||||
metadata = dict([(m['key'], m['value']) for m in metadata])
|
||||
_inject_file_into_fs(fs, 'meta.js', jsonutils.dumps(metadata))
|
||||
|
||||
|
||||
def _setup_selinux_for_keys(fs):
|
||||
def _setup_selinux_for_keys(fs, sshdir):
|
||||
"""Get selinux guests to ensure correct context on injected keys."""
|
||||
|
||||
se_cfg = _join_and_check_path_within_fs(fs, 'etc', 'selinux')
|
||||
se_cfg, _err = utils.trycmd('readlink', '-e', se_cfg, run_as_root=True)
|
||||
if not se_cfg:
|
||||
if not fs.has_file(os.path.join("etc", "selinux")):
|
||||
return
|
||||
|
||||
rclocal = _join_and_check_path_within_fs(fs, 'etc', 'rc.local')
|
||||
rclocal = os.path.join('etc', 'rc.local')
|
||||
rc_d = os.path.join('etc', 'rc.d')
|
||||
|
||||
# Support systemd based systems
|
||||
rc_d = _join_and_check_path_within_fs(fs, 'etc', 'rc.d')
|
||||
rclocal_e, _err = utils.trycmd('readlink', '-e', rclocal, run_as_root=True)
|
||||
rc_d_e, _err = utils.trycmd('readlink', '-e', rc_d, run_as_root=True)
|
||||
if not rclocal_e and rc_d_e:
|
||||
if not fs.has_file(rclocal) and fs.has_file(rc_d):
|
||||
rclocal = os.path.join(rc_d, 'rc.local')
|
||||
|
||||
# Note some systems end rc.local with "exit 0"
|
||||
|
@ -409,12 +389,11 @@ def _setup_selinux_for_keys(fs):
|
|||
restorecon = [
|
||||
'#!/bin/sh\n',
|
||||
'# Added by Nova to ensure injected ssh keys have the right context\n',
|
||||
'restorecon -RF /root/.ssh/ 2>/dev/null || :\n',
|
||||
'restorecon -RF %s 2>/dev/null || :\n' % sshdir,
|
||||
]
|
||||
|
||||
rclocal_rel = os.path.relpath(rclocal, fs)
|
||||
_inject_file_into_fs(fs, rclocal_rel, ''.join(restorecon), append=True)
|
||||
utils.execute('chmod', 'a+x', rclocal, run_as_root=True)
|
||||
_inject_file_into_fs(fs, rclocal, ''.join(restorecon), append=True)
|
||||
fs.set_permissions(rclocal, 0700)
|
||||
|
||||
|
||||
def _inject_key_into_fs(key, fs):
|
||||
|
@ -423,12 +402,15 @@ def _inject_key_into_fs(key, fs):
|
|||
key is an ssh key string.
|
||||
fs is the path to the base of the filesystem into which to inject the key.
|
||||
"""
|
||||
sshdir = _join_and_check_path_within_fs(fs, 'root', '.ssh')
|
||||
utils.execute('mkdir', '-p', sshdir, run_as_root=True)
|
||||
utils.execute('chown', 'root', sshdir, run_as_root=True)
|
||||
utils.execute('chmod', '700', sshdir, run_as_root=True)
|
||||
|
||||
keyfile = os.path.join('root', '.ssh', 'authorized_keys')
|
||||
LOG.debug(_("Inject key fs=%(fs)s key=%(key)s") %
|
||||
locals())
|
||||
sshdir = os.path.join('root', '.ssh')
|
||||
fs.make_path(sshdir)
|
||||
fs.set_ownership(sshdir, "root", "root")
|
||||
fs.set_permissions(sshdir, 0700)
|
||||
|
||||
keyfile = os.path.join(sshdir, 'authorized_keys')
|
||||
|
||||
key_data = ''.join([
|
||||
'\n',
|
||||
|
@ -440,7 +422,7 @@ def _inject_key_into_fs(key, fs):
|
|||
|
||||
_inject_file_into_fs(fs, keyfile, key_data, append=True)
|
||||
|
||||
_setup_selinux_for_keys(fs)
|
||||
_setup_selinux_for_keys(fs, sshdir)
|
||||
|
||||
|
||||
def _inject_net_into_fs(net, fs):
|
||||
|
@ -448,10 +430,13 @@ def _inject_net_into_fs(net, fs):
|
|||
|
||||
net is the contents of /etc/network/interfaces.
|
||||
"""
|
||||
netdir = _join_and_check_path_within_fs(fs, 'etc', 'network')
|
||||
utils.execute('mkdir', '-p', netdir, run_as_root=True)
|
||||
utils.execute('chown', 'root:root', netdir, run_as_root=True)
|
||||
utils.execute('chmod', 755, netdir, run_as_root=True)
|
||||
|
||||
LOG.debug(_("Inject key fs=%(fs)s net=%(net)s") %
|
||||
locals())
|
||||
netdir = os.path.join('etc', 'network')
|
||||
fs.make_path(netdir)
|
||||
fs.set_ownership(netdir, "root", "root")
|
||||
fs.set_permissions(netdir, 0744)
|
||||
|
||||
netfile = os.path.join('etc', 'network', 'interfaces')
|
||||
_inject_file_into_fs(fs, netfile, net)
|
||||
|
@ -472,6 +457,9 @@ def _inject_admin_password_into_fs(admin_passwd, fs):
|
|||
# files from the instance filesystem to local files, make any
|
||||
# necessary changes, and then copy them back.
|
||||
|
||||
LOG.debug(_("Inject admin password fs=%(fs)s "
|
||||
"admin_passwd=ha-ha-not-telling-you") %
|
||||
locals())
|
||||
admin_user = 'root'
|
||||
|
||||
fd, tmp_passwd = tempfile.mkstemp()
|
||||
|
@ -479,19 +467,27 @@ def _inject_admin_password_into_fs(admin_passwd, fs):
|
|||
fd, tmp_shadow = tempfile.mkstemp()
|
||||
os.close(fd)
|
||||
|
||||
passwd_path = _join_and_check_path_within_fs(fs, 'etc', 'passwd')
|
||||
shadow_path = _join_and_check_path_within_fs(fs, 'etc', 'shadow')
|
||||
passwd_path = os.path.join('etc', 'passwd')
|
||||
shadow_path = os.path.join('etc', 'shadow')
|
||||
|
||||
utils.execute('cp', passwd_path, tmp_passwd, run_as_root=True)
|
||||
utils.execute('cp', shadow_path, tmp_shadow, run_as_root=True)
|
||||
_set_passwd(admin_user, admin_passwd, tmp_passwd, tmp_shadow)
|
||||
utils.execute('cp', tmp_passwd, passwd_path, run_as_root=True)
|
||||
os.unlink(tmp_passwd)
|
||||
utils.execute('cp', tmp_shadow, shadow_path, run_as_root=True)
|
||||
os.unlink(tmp_shadow)
|
||||
passwd_data = fs.read_file(passwd_path)
|
||||
shadow_data = fs.read_file(shadow_path)
|
||||
|
||||
new_shadow_data = _set_passwd(admin_user, admin_passwd,
|
||||
passwd_data, shadow_data)
|
||||
|
||||
fs.replace_file(shadow_path, new_shadow_data)
|
||||
|
||||
|
||||
def _set_passwd(username, admin_passwd, passwd_file, shadow_file):
|
||||
def _generate_salt():
|
||||
salt_set = ('abcdefghijklmnopqrstuvwxyz'
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
'0123456789./')
|
||||
salt = 16 * ' '
|
||||
return ''.join([random.choice(salt_set) for c in salt])
|
||||
|
||||
|
||||
def _set_passwd(username, admin_passwd, passwd_data, shadow_data):
|
||||
"""set the password for username to admin_passwd
|
||||
|
||||
The passwd_file is not modified. The shadow_file is updated.
|
||||
|
@ -508,14 +504,10 @@ def _set_passwd(username, admin_passwd, passwd_file, shadow_file):
|
|||
if os.name == 'nt':
|
||||
raise exception.NovaException(_('Not implemented on Windows'))
|
||||
|
||||
salt_set = ('abcdefghijklmnopqrstuvwxyz'
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
'0123456789./')
|
||||
# encryption algo - id pairs for crypt()
|
||||
algos = {'SHA-512': '$6$', 'SHA-256': '$5$', 'MD5': '$1$', 'DES': ''}
|
||||
|
||||
salt = 16 * ' '
|
||||
salt = ''.join([random.choice(salt_set) for c in salt])
|
||||
salt = _generate_salt()
|
||||
|
||||
# crypt() depends on the underlying libc, and may not support all
|
||||
# forms of hash. We try md5 first. If we get only 13 characters back,
|
||||
|
@ -528,39 +520,34 @@ def _set_passwd(username, admin_passwd, passwd_file, shadow_file):
|
|||
if len(encrypted_passwd) == 13:
|
||||
encrypted_passwd = crypt.crypt(admin_passwd, algos['DES'] + salt)
|
||||
|
||||
try:
|
||||
p_file = open(passwd_file, 'rb')
|
||||
s_file = open(shadow_file, 'rb')
|
||||
p_file = passwd_data.split("\n")
|
||||
s_file = shadow_data.split("\n")
|
||||
|
||||
# username MUST exist in passwd file or it's an error
|
||||
found = False
|
||||
for entry in p_file:
|
||||
split_entry = entry.split(':')
|
||||
if split_entry[0] == username:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
msg = _('User %(username)s not found in password file.')
|
||||
raise exception.NovaException(msg % username)
|
||||
# username MUST exist in passwd file or it's an error
|
||||
found = False
|
||||
for entry in p_file:
|
||||
split_entry = entry.split(':')
|
||||
if split_entry[0] == username:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
msg = _('User %(username)s not found in password file.')
|
||||
raise exception.NovaException(msg % username)
|
||||
|
||||
# update password in the shadow file.It's an error if the
|
||||
# the user doesn't exist.
|
||||
new_shadow = list()
|
||||
found = False
|
||||
for entry in s_file:
|
||||
split_entry = entry.split(':')
|
||||
if split_entry[0] == username:
|
||||
split_entry[1] = encrypted_passwd
|
||||
found = True
|
||||
new_entry = ':'.join(split_entry)
|
||||
new_shadow.append(new_entry)
|
||||
s_file.close()
|
||||
if not found:
|
||||
msg = _('User %(username)s not found in shadow file.')
|
||||
raise exception.NovaException(msg % username)
|
||||
s_file = open(shadow_file, 'wb')
|
||||
for entry in new_shadow:
|
||||
s_file.write(entry)
|
||||
finally:
|
||||
p_file.close()
|
||||
s_file.close()
|
||||
# update password in the shadow file.It's an error if the
|
||||
# the user doesn't exist.
|
||||
new_shadow = list()
|
||||
found = False
|
||||
for entry in s_file:
|
||||
split_entry = entry.split(':')
|
||||
if split_entry[0] == username:
|
||||
split_entry[1] = encrypted_passwd
|
||||
found = True
|
||||
new_entry = ':'.join(split_entry)
|
||||
new_shadow.append(new_entry)
|
||||
|
||||
if not found:
|
||||
msg = _('User %(username)s not found in shadow file.')
|
||||
raise exception.NovaException(msg % username)
|
||||
|
||||
return "\n".join(new_shadow)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from nova.openstack.common import importutils
|
||||
from nova.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
@ -21,6 +22,30 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
class VFS(object):
|
||||
|
||||
@staticmethod
|
||||
def instance_for_image(imgfile, imgfmt, partition):
|
||||
LOG.debug(_("Instance for image imgfile=%(imgfile)s "
|
||||
"imgfmt=%(imgfmt)s partition=%(partition)s")
|
||||
% locals())
|
||||
hasGuestfs = False
|
||||
try:
|
||||
LOG.debug(_("Trying to import guestfs"))
|
||||
importutils.import_module("guestfs")
|
||||
hasGuestfs = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if hasGuestfs:
|
||||
LOG.debug(_("Using primary VFSGuestFS"))
|
||||
return importutils.import_object(
|
||||
"nova.virt.disk.vfs.guestfs.VFSGuestFS",
|
||||
imgfile, imgfmt, partition)
|
||||
else:
|
||||
LOG.debug(_("Falling back to VFSLocalFS"))
|
||||
return importutils.import_object(
|
||||
"nova.virt.disk.vfs.localfs.VFSLocalFS",
|
||||
imgfile, imgfmt, partition)
|
||||
|
||||
"""
|
||||
The VFS class defines an interface for manipulating files within
|
||||
a virtual disk image filesystem. This allows file injection code
|
||||
|
|
|
@ -44,6 +44,7 @@ from nova.openstack.common import excutils
|
|||
from nova.openstack.common import log as logging
|
||||
from nova import utils
|
||||
from nova.virt.disk import api as disk
|
||||
from nova.virt.disk.vfs import localfs as vfsimpl
|
||||
from nova.virt import driver
|
||||
from nova.virt.xenapi import agent
|
||||
from nova.virt.xenapi import volume_utils
|
||||
|
@ -2106,11 +2107,14 @@ def _mounted_processing(device, key, net, metadata):
|
|||
try:
|
||||
# This try block ensures that the umount occurs
|
||||
if not agent.find_guest_agent(tmpdir):
|
||||
vfs = vfsimpl.VFSLocalFS(imgfile=None,
|
||||
imgfmt=None,
|
||||
imgdir=tmpdir)
|
||||
LOG.info(_('Manipulating interface files directly'))
|
||||
# for xenapi, we don't 'inject' admin_password here,
|
||||
# it's handled at instance startup time, nor do we
|
||||
# support injecting arbitrary files here.
|
||||
disk.inject_data_into_fs(tmpdir,
|
||||
disk.inject_data_into_fs(vfs,
|
||||
key, net, metadata, None, None)
|
||||
finally:
|
||||
utils.execute('umount', dev_path, run_as_root=True)
|
||||
|
|
Loading…
Reference in New Issue