# 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 fixtures
import os
import sys

from posix import stat_result

from nova import exception
from nova import test
from nova.tests import fakeguestfs
from nova.virt.disk import api as diskapi
from nova.virt.disk.vfs import guestfs as vfsguestfs


class VirtDiskTest(test.NoDBTestCase):

    def setUp(self):
        super(VirtDiskTest, self).setUp()
        sys.modules['guestfs'] = fakeguestfs
        vfsguestfs.guestfs = fakeguestfs

    def test_inject_data(self):

        orig_os_stat = os.stat

        def fake_stat(arg):
            if arg == '/some/file':  # fake success
                return stat_result((16877, 2, 2049L,
                                    23, 0, 0,
                                    4096, 1381787843,
                                    1381635971, 1381635971))
            else:
                return orig_os_stat(arg)

        self.useFixture(fixtures.MonkeyPatch('os.stat', fake_stat))

        self.assertTrue(diskapi.inject_data("/some/file", use_cow=True))

        self.assertTrue(diskapi.inject_data("/some/file",
                                            mandatory=('files',)))

        self.assertTrue(diskapi.inject_data("/some/file", key="mysshkey",
                                            mandatory=('key',)))

        os_name = os.name
        os.name = 'nt'  # Cause password injection to fail
        self.assertRaises(exception.NovaException,
                          diskapi.inject_data,
                          "/some/file", admin_password="p",
                          mandatory=('admin_password',))
        self.assertFalse(diskapi.inject_data("/some/file", admin_password="p"))
        os.name = os_name

        self.assertFalse(diskapi.inject_data("/some/fail/file"))

    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': 0o700})
        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': 0o600})

        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': 0o700})

        self.assertTrue("/root/.ssh" in vfs.handle.files)
        self.assertEquals(vfs.handle.files["/root/.ssh"],
                          {'isdir': True, 'gid': 0, 'uid': 0, 'mode': 0o700})
        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': 0o600})

        vfs.teardown()

    def test_inject_data_key_with_selinux_append_with_newline(self):

        vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
        vfs.setup()

        vfs.replace_file("/etc/rc.d/rc.local", "#!/bin/sh\necho done")
        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': "#!/bin/sh\necho done\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': 0o700})
        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': 0o700,
                           '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': 0o700,
                           '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': 0o700,
                           'uid': 100})
        shadow = vfs.handle.files["/etc/shadow"]

        # if the encrypted password is only 13 characters long, then
        # nova.virt.disk.api:_set_password fell back to DES.
        if len(shadow['content']) == 91:
            self.assertEquals(shadow,
                              {'content': "root:12tir.zIbWQ3c" +
                                          ":14917:0:99999:7:::\n" +
                                          "bin:*:14495:0:99999:7:::\n" +
                                          "daemon:*:14495:0:99999:7:::\n",
                               'gid': 100,
                               'isdir': False,
                               'mode': 0o700,
                               'uid': 100})
        else:
            self.assertEquals(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': 0o700,
                               'uid': 100})
        vfs.teardown()
