diff --git a/actions/cockpit.socket b/actions/cockpit.socket new file mode 100755 index 000000000..7fa660d6d --- /dev/null +++ b/actions/cockpit.socket @@ -0,0 +1,110 @@ +#!/usr/bin/python3 +# -*- mode: python -*- +# +# This file is part of Plinth. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +Configuration helper for Cockpit. +""" + +import argparse +import augeas +import subprocess + +from plinth import action_utils + +CONFIG_FILE = '/etc/cockpit/cockpit.conf' + +def parse_arguments(): + """Return parsed command line arguments as dictionary.""" + parser = argparse.ArgumentParser() + subparsers = parser.add_subparsers(dest='subcommand', help='Sub command') + + subparsers.add_parser('pre-setup', help='Perform pre-setup operations') + subparsers.add_parser('setup', help='Setup Cockpit configuration') + subparsers.add_parser('enable', help='Enable Cockpit site') + subparsers.add_parser('disable', help='Disable Cockpit site') + + subparsers.required = True + return parser.parse_args() + + +def subcommand_pre_setup(_): + """Preseed debconf values before packages are installed.""" + """subprocess.check_output( + ['debconf-set-selections'], + input=b'cockpit tt-rss/database-type string pgsql')""" + pass + +def subcommand_setup(_): + """Setup Cockpit configuration.""" + + open(CONFIG_FILE, 'a').close() + + aug = load_augeas() + SECTION = CONFIG_FILE+'/WebService' + aug.set('/files'+SECTION, '') + origins = "https://localhost:9090 https://localhost:4430" + aug.set('/files'+SECTION+'/Origins', origins) + aug.save() + + action_utils.webserver_enable('proxy_wstunnel', kind='module') + action_utils.service_restart('cockpit.socket') + action_utils.webserver_enable('cockpit-plinth') + action_utils.service_restart('apache2') + +def subcommand_enable(_): + """Enable web configuration and reload.""" + action_utils.service_enable('cockpit.socket') +# action_utils.webserver_enable('cockpit-plinth') + + +def subcommand_disable(_): + """Disable web configuration and reload.""" +# action_utils.webserver_disable('cockpit-plinth') + action_utils.service_disable('cockpit.socket') + +def subcommand_add_domain(_): + aug = load_augeas() + +def subcommand_remove_domain(_): + aug = load_augeas() + +def subcommand_change_domain(_): + aug = load_augeas() + +def load_augeas(): + """Initialize Augeas.""" + aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD + + augeas.Augeas.NO_MODL_AUTOLOAD) + aug.set('/augeas/load/inifile/lens', 'Puppet.lns') + aug.set('/augeas/load/inifile/incl[last() + 1]', CONFIG_FILE) + aug.load() + return aug + + +def main(): + """Parse arguments and perform all duties.""" + arguments = parse_arguments() + + subcommand = arguments.subcommand.replace('-', '_') + subcommand_method = globals()['subcommand_' + subcommand] + subcommand_method(arguments) + + +if __name__ == '__main__': + main() diff --git a/data/etc/apache2/conf-available/cockpit-plinth.conf b/data/etc/apache2/conf-available/cockpit-plinth.conf new file mode 100644 index 000000000..9509a0133 --- /dev/null +++ b/data/etc/apache2/conf-available/cockpit-plinth.conf @@ -0,0 +1,21 @@ +## +## On all sites, provide cockpit on a defaut path: /cockpit +## +## Requires the following Apache modules to be enabled: +## mod_headers +## mod_proxy +## mod_proxy_http +## mod_proxy_wstunnel +## + + + proxypass http://localhost:9090/ + + + + proxypass http://localhost:9090/cockpit/ + + + + proxypass ws://localhost:9090/cockpit/socket + diff --git a/data/etc/plinth/modules-enabled/cockpit b/data/etc/plinth/modules-enabled/cockpit new file mode 100644 index 000000000..b4e16954d --- /dev/null +++ b/data/etc/plinth/modules-enabled/cockpit @@ -0,0 +1 @@ +plinth.modules.cockpit diff --git a/plinth/modules/cockpit/__init__.py b/plinth/modules/cockpit/__init__.py new file mode 100644 index 000000000..dd44b8760 --- /dev/null +++ b/plinth/modules/cockpit/__init__.py @@ -0,0 +1,133 @@ +# +# This file is part of Plinth. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +Plinth module to configure Cockpit. +""" + +from django.utils.translation import ugettext_lazy as _ +from plinth.utils import format_lazy + +from plinth import actions +from plinth import action_utils +from plinth import cfg +from plinth import frontpage +from plinth import service as service_module +from plinth.menu import main_menu +from plinth.signals import domain_added, domain_removed, domainname_change + + +version = 1 + +managed_services = ['cockpit.socket'] + +managed_packages = ['cockpit'] + +title = _('Dashboard of Servers \n (Cockpit)') + +description = [ + _('Cockpit is an interactive server admin interface. It is easy to use ' + 'and very light weight. Cockpit interacts directly with the operating ' + 'system from a real linux session in a browser'), + + format_lazy( + _('When enabled, Cockpit will be available from ' + '/cockpit path on the web server. It can be accessed by ' + 'any user with a {box_name} login.'), + box_name=_(cfg.box_name)) +] + +service = None + + +def init(): + """Intialize the module.""" + menu = main_menu.get('apps') + menu.add_urlname(title, 'glyphicon-dashboard', 'cockpit:index') + + global service + setup_helper = globals()['setup_helper'] + if setup_helper.get_state() != 'needs-setup': + service = service_module.Service( + managed_services[0], title, ports=['http', 'https'], + is_external=True, + is_enabled=is_enabled, enable=enable, disable=disable) + + if is_enabled(): + add_shortcut() + + +def setup(helper, old_version=None): + """Install and configure the module.""" + helper.call('pre', actions.superuser_run, 'cockpit', ['pre-setup']) + helper.install(managed_packages) + helper.call('post', actions.superuser_run, 'cockpit', ['setup']) + global service + if service is None: + service = service_module.Service( + managed_services[0], title, ports=['http', 'https'], + is_external=True, + is_enabled=is_enabled, enable=enable, disable=disable) + helper.call('post', service.notify_enabled, None, True) + helper.call('post', add_shortcut) + + +def add_shortcut(): + frontpage.add_shortcut('cockpit', title, url='/cockpit', + login_required=True) + + +def is_enabled(): + """Return whether the module is enabled.""" + return (action_utils.webserver_is_enabled('cockpit-plinth') and + action_utils.service_is_running('cockpit.socket')) + + +def enable(): + """Enable the module.""" + actions.superuser_run('cockpit.socket', ['enable']) + add_shortcut() + + +def disable(): + """Disable the module.""" + actions.superuser_run('cockpit.socket', ['disable']) + frontpage.remove_shortcut('cockpit') + + +def diagnose(): + """Run diagnostics and return the results.""" + results = [] + + results.extend(action_utils.diagnose_url_on_all( + 'https://{host}/cockpit', check_certificate=False)) + + return results + +def on_domain_added(): + actions.superuser_run('cockpit.socket', ['add_domain']) + +def on_domain_removed(): + actions.superuser_run('cockpit.socket', ['remove_domain']) + +def on_domainname_change(sender, old_domainname, new_domainname, **kwargs): + del sender + del kwargs + actions.superuser_run('cockpit.socket', + ['change_domain', + '--domainname', new_domainname], + async=True) diff --git a/plinth/modules/cockpit/tests/__init__.py b/plinth/modules/cockpit/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/plinth/modules/cockpit/urls.py b/plinth/modules/cockpit/urls.py new file mode 100644 index 000000000..72a9e8096 --- /dev/null +++ b/plinth/modules/cockpit/urls.py @@ -0,0 +1,35 @@ +# +# This file is part of Plinth. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +""" +URLs for Cockpit module. +""" + +from django.conf.urls import url + +from plinth.views import ServiceView +from plinth.modules import cockpit + + +urlpatterns = [ + url(r'^apps/cockpit/$', ServiceView.as_view( + service_id=cockpit.managed_services[0], + diagnostics_module_name="cockpit", + description=cockpit.description, + show_status_block=True + ), name='index'), +] diff --git a/static/themes/default/icons/cockpit.png b/static/themes/default/icons/cockpit.png new file mode 100644 index 000000000..86195c4aa Binary files /dev/null and b/static/themes/default/icons/cockpit.png differ