From 44cf56c2225ac97bc3415a6c83d40decfad7d78f Mon Sep 17 00:00:00 2001 From: vignanl Date: Thu, 10 Aug 2017 07:50:18 +0530 Subject: [PATCH] cockpit: New module for server administration Reviewed-by: Sunil Mohan Adapa --- actions/cockpit.socket | 110 +++++++++++++++ .../conf-available/cockpit-plinth.conf | 21 +++ data/etc/plinth/modules-enabled/cockpit | 1 + plinth/modules/cockpit/__init__.py | 133 ++++++++++++++++++ plinth/modules/cockpit/tests/__init__.py | 0 plinth/modules/cockpit/urls.py | 35 +++++ static/themes/default/icons/cockpit.png | Bin 0 -> 6166 bytes 7 files changed, 300 insertions(+) create mode 100755 actions/cockpit.socket create mode 100644 data/etc/apache2/conf-available/cockpit-plinth.conf create mode 100644 data/etc/plinth/modules-enabled/cockpit create mode 100644 plinth/modules/cockpit/__init__.py create mode 100644 plinth/modules/cockpit/tests/__init__.py create mode 100644 plinth/modules/cockpit/urls.py create mode 100644 static/themes/default/icons/cockpit.png 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 0000000000000000000000000000000000000000..86195c4aa341c8ae6869a600089db368fcc5ffd1 GIT binary patch literal 6166 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 Ub^4