#!/usr/bin/python
# -*- coding: utf-8 -*-

# This file is part of Cockpit.
#
# Copyright (C) 2013 Red Hat, Inc.
#
# Cockpit is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Cockpit is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.

import parent
from testlib import *

def wait_dashboard_addresses(b, expected):
    b.wait_js_func(
        """(function(expected) {
            var nodes = document.querySelectorAll('#dashboard-hosts .list-group-item');
            var addresses = Array.prototype.map.call(nodes, function(e) {
                return e.getAttribute("data-address");
            });
            return expected.sort().toString() == addresses.sort().toString();
        })""", expected)

def old_add_machine(b, address):
    b.click('#dashboard-add')
    b.wait_popup('dashboard_setup_server_dialog')
    b.set_val('#dashboard_setup_address', address)
    b.wait_text('#dashboard_setup_next', "Next")
    b.click('#dashboard_setup_next')
    b.wait_text('#dashboard_setup_next', "Add host")
    b.click('#dashboard_setup_next')
    b.wait_text('#dashboard_setup_next', "Close")
    b.click('#dashboard_setup_next')
    b.wait_popdown('dashboard_setup_server_dialog')

def new_add_machine(b, address):
    b.click('#dashboard-add')
    b.wait_popup('dashboard_setup_server_dialog')
    b.set_val('#add-machine-address', address)

    b.wait_text('#dashboard_setup_server_dialog .btn-primary', "Add")
    b.wait_present("#dashboard_setup_server_dialog .btn-primary:not([disabled])")
    b.click('#dashboard_setup_server_dialog .btn-primary')
    b.wait_in_text('#dashboard_setup_server_dialog', "Fingerprint")
    b.click('#dashboard_setup_server_dialog .btn-primary')
    b.wait_popdown('dashboard_setup_server_dialog')

class TestMultiOS(MachineCase):
    additional_machines = {
        'fedora-stock': { 'machine': { 'image': 'fedora-stock' } }
    }

    def check_spawn(self, b, address):
        result = b.call_js_func("""(function(address) {
            return cockpit.spawn(['echo', 'hi'], { host: address });
        })""", address)
        self.assertEqual(result, "hi\n")

    def check_dbus(self, b, address):
        b.call_js_func("""(function(address) {
            return cockpit.dbus("org.freedesktop.DBus", { host: address })
                .proxy("org.freedesktop.DBus", "/").call("GetId");
        })""", address)

    def checkStock(self):
        dev_m = self.machine
        dev_b = self.browser

        # Newer bridges running under an old cockpit-ws don't get the
        # support that they need to prompt for the password. So open
        # up sudo for this test.
        dev_m.execute("echo '%wheel ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/user-override")
        dev_m.execute("echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/user-override")

        self.allow_hostkey_messages()

        self.login_and_go("/dashboard")

        dev_dashboard_addresses = [ "localhost" ]
        wait_dashboard_addresses (dev_b, dev_dashboard_addresses)

        def stock_login_and_go(browser, page, href, host="localhost"):
            if host:
                browser.open("/#" + "/@" + host + href)
            else:
                browser.open("/#" + href)
            browser.wait_visible("#login")
            browser.set_val('#login-user-input', browser.default_user)
            browser.set_val('#login-password-input', "foobar")
            browser.click('#login-button')
            browser.expect_load()
            browser.wait_present('#content')
            browser.wait_visible('#content')
            if page:
                stock_enter_page(browser, page, host=host)

        def stock_enter_page(browser, page, host="localhost"):
            if host:
                frame = host + "/shell/shell"
            else:
                frame = "localhost/shell/shell"
            browser.switch_to_top()
            browser.wait_present("iframe.container-frame[name='%s'][data-loaded]" % frame)
            browser.wait_visible("iframe.container-frame[name='%s']" % frame)
            browser.switch_to_frame(frame)
            browser.wait_visible('#' + page)

        stock_m = self.machines['fedora-stock']
        stock_b = self.new_browser(stock_m.address)

        stock_login_and_go(stock_b, "dashboard", href="/dashboard/list", host=None)
        wait_dashboard_addresses (stock_b, [ "localhost" ])

        old_add_machine(stock_b, dev_m.address)
        wait_dashboard_addresses (stock_b, [ "localhost", dev_m.address ])

        dev_b.switch_to_top()
        dev_b.switch_to_frame("cockpit1:localhost/dashboard")

        new_add_machine(dev_b, stock_m.address)
        dev_dashboard_addresses.append(stock_m.address)
        wait_dashboard_addresses (dev_b, dev_dashboard_addresses)

        self.check_dbus(stock_b, dev_m.address)
        self.check_dbus(dev_b, stock_m.address)

        self.check_spawn(stock_b, dev_m.address)
        self.check_spawn(dev_b, stock_m.address)

        dev_b.switch_to_top()
        dev_b.go("/@%s/network/interfaces" % stock_m.address)
        dev_b.wait_present("iframe.container-frame[name='cockpit1:%s/shell/shell'][src$='#/networking']" % stock_m.address)

        dev_b.switch_to_top()
        dev_b.go("/@%s/storage/devices" % stock_m.address)
        dev_b.wait_present("iframe.container-frame[name='cockpit1:%s/shell/shell'][src$='#/storage']" % stock_m.address)

        dev_b.switch_to_top()
        dev_b.go("/@%s/users/local" % stock_m.address)
        dev_b.wait_present("iframe.container-frame[name='cockpit1:%s/shell/shell'][src$='#/accounts']" % stock_m.address)

        stock_b.switch_to_top()
        stock_b.go("#/@%s/system/index" % dev_m.address)
        stock_b.wait_present("iframe.container-frame[name='%s/system/index']" % dev_m.address)
        stock_b.wait_present("iframe.container-frame[name='%s/system/index'][data-loaded]" % dev_m.address)
        stock_b.switch_to_frame('%s/system/index' % dev_m.address)
        stock_b.wait_present("#system_information_hardware_text")
        stock_b.wait_text_not("#system_information_hardware_text", "")

        # Shows a curtains when accessing to default page from old system
        stock_b.switch_to_top()
        stock_b.go("#/@%s" % dev_m.address)
        stock_b.wait_present("iframe.container-frame[name='%s/shell/shell']" % dev_m.address)
        stock_b.wait_present("iframe.container-frame[name='%s/shell/shell'][data-loaded]" % dev_m.address)
        stock_b.switch_to_frame('%s/shell/shell' % dev_m.address)
        stock_b.wait_present(".curtains-ct")

        # Messages from previous versions of cockpit
        self.allow_journal_messages("g_hash_table_iter_next: assertion 'ri->version == ri->hash_table->version' failed")
        self.allow_journal_messages("couldn't run usermod command: Child process exited with code 6")
        self.allow_journal_messages("usermod: user 'postfix' does not exist")
        self.allow_journal_messages(".*pam_authenticate failed: Authentication failure")

    @skipImage("No dashboard on Atomic", "continuous-atomic", "fedora-atomic", "rhel-atomic")
    def testFedora22(self):
        try:
            self.checkStock()
        except Error, e:
            # Old versions of Cockpit in Fedora 22 have a race which throws an exception
            if e.msg != "Error: TypeError: null is not an object (evaluating 'o.client.lookup')":
                raise

class TestMultiOSDirect(MachineCase):
    additional_machines = {
        'fedora-23-stock': { 'machine': { 'image': 'fedora-23-stock' } }
    }

    @skipImage("No dashboard on Atomic", "continuous-atomic", "fedora-atomic", "rhel-atomic")
    def testFedora23Direct(self):
        b = self.browser

        self.allow_hostkey_messages()

        self.login_and_go("/dashboard")

        dev_dashboard_addresses = [ "localhost" ]
        wait_dashboard_addresses (b, dev_dashboard_addresses)

        stock_m = self.machines['fedora-23-stock']
        stock_m.execute("hostnamectl set-hostname stock")

        new_add_machine(b, stock_m.address)
        dev_dashboard_addresses.append(stock_m.address)
        wait_dashboard_addresses (b, dev_dashboard_addresses)
        b.logout()

        # Access stock directly from dev
        b.open("/={}".format(stock_m.address))
        b.wait_visible("#login")
        b.wait_not_visible("#badge")
        b.wait_not_visible("#brand")
        b.set_val("#login-user-input", "admin")
        b.set_val("#login-password-input", "foobar")
        b.click('#login-button')
        b.expect_load()

        b.wait_present("iframe.container-frame[name='cockpit1:localhost/system'][data-loaded]")
        b.wait_not_visible(".curtains")
        b.wait_visible("iframe.container-frame[name='cockpit1:localhost/system']")
        b.switch_to_frame("cockpit1:localhost/system")
        b.wait_present("body")
        b.wait_visible("body")
        b.wait_in_text('#system_information_hostname_button', "stock")
        b.switch_to_top()

        # Branding uses default because there is no os information
        b.wait_in_text("#index-brand", "Cockpit")
        b.wait_js_cond('window.location.pathname.indexOf("shell/index.html") > -1')

if __name__ == '__main__':
    test_main()
