diff --git a/createlinks.pl b/createlinks.pl new file mode 100644 index 0000000..66923f1 --- /dev/null +++ b/createlinks.pl @@ -0,0 +1,15 @@ +#!/usr/bin/perl -w + +use esmith::Build::CreateLinks qw(:all); +use File::Basename; +use File::Path; + + +$event = "doraemon-reboot"; +event_actions($event, 'doraemon-reboot' => 50); + +$event = "doraemon-shutdown"; +event_actions($event, 'doraemon-shutdown' => 50); + +$event = "doraemon-wake"; +event_actions($event, 'doraemon-wake' => 50); diff --git a/defaults/access b/defaults/access deleted file mode 100644 index 3e18ebf..0000000 --- a/defaults/access +++ /dev/null @@ -1 +0,0 @@ -private diff --git a/doraemon.ini b/doraemon.ini deleted file mode 100644 index a9bbdc8..0000000 --- a/doraemon.ini +++ /dev/null @@ -1,16 +0,0 @@ -[Daemon] -Database = /var/lib/doraemon/doraemon.db -LogFile = /var/log/doraemon.log -PIDFile = /var/run/doraemon.pid -BindAddress = 0.0.0.0 -Port = 3000 - -[NameSettings] -Base = lab -Role = client -Digits = 2 - -[Files] -Domain = /etc/domain.yml -MgmtKey = /home/amgmt/.ssh/id_rsa.pub -VaultPassFile = /home/amgmt/.ansible/vault.txt diff --git a/doraemon.logrotate b/doraemon.logrotate deleted file mode 100644 index 443f997..0000000 --- a/doraemon.logrotate +++ /dev/null @@ -1,6 +0,0 @@ -/var/log/doraemon.log { - daily - rotate 15 - compress - missingok -} diff --git a/doraemon.py b/doraemon.py deleted file mode 100644 index 459c316..0000000 --- a/doraemon.py +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -"""Helps a client to join domain and maintain itself.""" -__author__ = 'BgLUG' -__email__ = 'info@bglug.it' - -from contextlib import contextmanager, closing -from subprocess import Popen, PIPE -from sqlite3 import connect -from bottle import Bottle, request -from json import dumps -from ConfigParser import ConfigParser -from Crypto.Hash import MD5 -from Crypto.Cipher import AES -import base64 -import json -import sys -import re -import os - -class MyApp: - def __init__(self, configfile = '/etc/mac2hostname.ini'): - if not os.path.isfile(configfile): - print "Cannot find main file, %s. Exiting." % configfile - sys.exit(1) - - # Loads configurations - config = ConfigParser() - config.read(configfile) - # Populates private properties with configuration - self.__dbfile = config.get('Daemon', 'Database') if config.has_option('Daemon', 'Database') else '/var/lib/doraemon/doraemon.db' - self.__logdir = config.get('Daemon', 'LogFile') if config.has_option('Daemon', 'LogFile') else '/var/log/doraemon.log' - self.__pidfile = config.get('Daemon', 'PIDFile') if config.has_option('Daemon', 'PIDFile') else '/var/run/doraemon.pid' - self.__bindaddress = config.get('Daemon', 'BindAddress') if config.has_option('Daemon', 'BindAddress') else '127.0.0.1' - self.__port = config.getint('Daemon', 'Port') if config.has_option('Daemon', - 'Port') else 8080 - self.__defaultbase = config.get('NameSettings', 'Base') if config.has_option('NameSettings', 'Base') else 'client' - self.__defaultrole = config.get('NameSettings', 'Role') if config.has_option('NameSettings', 'Role') else 'client' - self.__namedigits = config.get('NameSettings', 'Digits') if config.has_option('NameSettings', 'Digits') else '2' - self.__domainfile = config.get('Files', 'Domain') if config.has_option('Files', 'Domain') else None - self.__mgmtfile = config.get('Files', 'MgmtKey') if config.has_option('Files', 'MgmtKey') else None - self.__vaultpassfile = config.get('Files', 'VaultPassFile') if config.has_option('Files', 'VaultPassFile') else None - self.__app = Bottle() - # Applies routes - self.__route() - # Assures table creation - self.__init_tables() - - @contextmanager - def __getcursor(self): - with connect(self.__dbfile) as connection: - with closing(connection.cursor()) as cursor: - yield cursor - - # Private methods - def __route(self): - self.__app.get('/mac2hostname', callback=self.mac2hostname) - self.__app.get('/whatsmyhostname', callback=self.whatsmyhostname) - self.__app.get('/hosts', callback=self.hosts) - self.__app.get('/domain', callback=self.domain) - self.__app.get('/mgmtkey', callback=self.mgmtkey) - self.__app.get('/vaultpass', callback=self.vaultpass) - self.__app.get('/ansible_list', callback=self.ansible_list) - self.__app.get('/ansible_host', callback=self.ansible_host) - self.__app.get('/epoptes-srv', callback=self.epoptes_srv) - - def __init_tables(self): - with self.__getcursor() as cursor: - cursor.execute('CREATE TABLE IF NOT EXISTS client (id INT PRIMARY KEY,' - 'hostname TEXT NOT NULL UNIQUE, mac TEXT UNIQUE, role TEXT)') - cursor.execute('CREATE INDEX IF NOT EXISTS idxmac ON client(mac)') - - def __getmac(self, ip): - Popen(['ping', '-c1', '-t2', ip], stdout=PIPE).communicate() - arp = Popen(['arp', '-n', ip], stdout=PIPE).communicate()[0] - return re.search(r'(([\da-fA-F]{1,2}\:){5}[\da-fA-F]{1,2})', arp).group(1).lower() - - def __normalizemac(self, mac): - return ':'.join(x.zfill(2) for x in mac.split('_')).lower() - - def __gethostname(self, mac, base = None, role = None): - if base == None: - base = self.__defaultbase - if role == None: - role = self.__defaultrole - - with self.__getcursor() as cursor: - (newid,) = cursor.execute('SELECT COALESCE(MAX(id) + 1, 1) FROM client').fetchone() - # Constucts the hostname format - formatstring = '%s-%0' + self.__namedigits + 'd' - data = (newid, formatstring % (base, newid), mac, role) - # Since MAC is Unique, this fails with the same MAC address - cursor.execute('INSERT OR IGNORE INTO client VALUES (?, ?, ?, ?)', data) - (hostname,) = cursor.execute('SELECT hostname FROM client WHERE mac = "%s"' % mac).fetchone() - return hostname - - # This should be used only after registration of the client - # (whatsmyhostname) - def __getrole(self): - macaddress = self.__normalizemac(self.__getmac(request['REMOTE_ADDR'])) - hostname = self.__gethostname(macaddress) - - with self.__getcursor() as cursor: - (role,) = cursor.execute("SELECT role FROM client WHERE hostname = '%s' AND mac = '%s'" % (hostname, macaddress)).fetchone() - return role - - # Variables returned to the client - def __rolevars(self, role=None): - retval = {'role': role, 'addpkg': [], 'delpkg': []} - try: - db = Popen(['/sbin/e-smith/db', 'roles', 'getjson'], stdout=PIPE).communicate()[0] - for data in json.loads(db): - if data['name'] == role: - retval['addpkg'] = re.split(r'[\s,;]+', data['props']['Addpkg']) - retval['delpkg'] = re.split(r'[\s,;]+', data['props']['Delpkg']) - continue - except: pass - return retval - - # Instance methods AKA routes - def mac2hostname(self): - # Ensure required parameters have been passed - if not request.query.mac: - return "Usage: GET /mac2hostname?mac=XX_XX_XX_XX_XX_XX[&base=YYY][&role=ZZZ]" - # Sets up variables for possible parameters - mac = self.__normalizemac(request.query.mac) - base = request.query.base or self.__defaultbase - role = request.query.role or self.__defaultrole - return self.__gethostname(mac, base, role) - - def whatsmyhostname(self): - # No required parameters - ip = request.query.ip or request['REMOTE_ADDR'] - base = request.query.base or self.__defaultbase - role = request.query.role or self.__defaultrole - return self.__gethostname(self.__getmac(ip), base, role) - - def hosts(self): - # Default where clause: no where specifications - where = '' - # If a role parameter is passed, list the hosts for that role - if request.query.role: - where = "WHERE role = '%s'" % request.query.role - with self.__getcursor() as cursor: - return dumps([dict((meta[0], data) - for meta, data in zip(cursor.description, row)) - for row in cursor.execute('SELECT role, hostname, mac FROM client ' - + where + 'ORDER BY role ASC, hostname ASC')], indent=4) - - def domain(self): - with open(self.__domainfile, 'r') as f: - return f.read() - - def mgmtkey(self): - with open(self.__mgmtfile, 'r') as f: - return f.read() - - def vaultpass(self): - with open(self.__vaultpassfile, 'r') as f: - ip = request.query.ip or request['REMOTE_ADDR'] - base = request.query.base or self.__defaultbase - role = request.query.role or self.__defaultrole - hostname = self.__gethostname(self.__getmac(ip), base, role) - # Encryption key - key = MD5.new(hostname).digest() - # Secret - secret = f.read().strip() - # Creating a secret that is multiple of 16 in length - i = 16 - (len(secret) % 16) - lengthy_secret = secret + i * 'x' - cipher = AES.new(key, AES.MODE_ECB) - crypted = cipher.encrypt(lengthy_secret) - # Encode in base64 because of unicode strings - return base64.b64encode(crypted) - - def ansible_list(self): - # Faking calls - if request.query.role: - role = request.query.role - else: - role = self.__getrole() - - result = { - 'localhost': { - 'hosts': [ 'localhost' ], - 'vars': { - 'ansible_connection': 'local' - } - }, - '_meta': { - 'hostvars': { - 'localhost': self.__rolevars(role) - } - } - } - return dumps(result) - - def ansible_host(self): - # Ignoring 'host' parameter, since it should always be 'localhost'. - # Instead, for testing purposes, if asked for a role, use it - if request.query.role: - role = request.query.role - else: - role = self.__getrole() - return dumps(self.__rolevars(role)) - - def epoptes_srv(self): - hostname = None - try: - with self.__getcursor() as cursor: - (hostname,) = cursor.execute('SELECT hostname FROM client WHERE role = \'docenti\' ORDER BY id DESC LIMIT 1').fetchone() - except TypeError: - return 'none' - - return hostname - - def start(self): - # Opens up a PID file - pid = os.getpid() - pidfile = open(self.__pidfile, 'w') - pidfile.write('%s' % pid) - pidfile.close() - - # Runs Bottle, at last. - self.__app.run(host=self.__bindaddress, port=self.__port) - -# Main body -if __name__ == '__main__': - cfgfile = '/etc/doraemon.ini' - if len(sys.argv) > 1: - cfgfile = sys.argv[1] - app = MyApp(cfgfile) - app.start() diff --git a/doraemon.spec b/doraemon.spec index 9923f8e..c79d813 100644 --- a/doraemon.spec +++ b/doraemon.spec @@ -1,85 +1,160 @@ Summary: Helps client to join domain and maintain itself Name: doraemon -Version: 1.2.1 -Release: 2.ns6 +Version: 2.0.0 +Release: 8.ns6 URL: https://github.com/bglug-it/doraemon/ License: GPLv2+ +Packager: Paolo Asperti Group: System Environment/Daemons BuildRoot: %{_tmppath}/%{name}-root -Requires: python python-bottle python-crypto2.6 nethserver-base -Requires(post): chkconfig nethserver-base -Requires(preun): chkconfig initscripts nethserver-base -Source0: doraemon-1.2.1.tar.gz +BuildRequires: nethserver-devtools > 1.0.1 +BuildRequires: hunspell-en +Requires: httpd, php, sudo, php-xml, php-mcrypt +Requires: nethserver-base, nethserver-php +Requires: net-tools +Requires: upstart +Source0: %{name}-%{version}.tar.gz BuildArch: noarch %description Helps client on a domain network to get information for its maintenance. %prep -%setup -q +%setup %build +%{makedocs} +perl createlinks.pl %install -rm -rf %{buildroot} -install -d %{buildroot} -install -d -m 755 %{buildroot}%{_sysconfdir} -install -m 644 doraemon.ini %{buildroot}%{_sysconfdir}/%{name}.ini -# Installo il servizio -install -d -m 755 %{buildroot}%{_initrddir} -install -m 755 initrd %{buildroot}%{_initrddir}/%{name} -# Installo lo script vero e proprio -install -d -m 755 %{buildroot}%{_bindir} -install -m 755 doraemon.py %{buildroot}%{_bindir}/%{name}.py -# Cartella del database -install -d %{buildroot}%{_sharedstatedir}/%{name} -# File per il rotate -install -d %{buildroot}%{_sysconfdir}/logrotate.d -install -m 644 doraemon.logrotate %{buildroot}%{_sysconfdir}/logrotate.d/%{name} -# File di Nethserver -install -d %{buildroot}%{_sysconfdir}/e-smith/db/configuration/defaults/%{name} -install -m 644 type %{buildroot}%{_sysconfdir}/e-smith/db/configuration/defaults/%{name}/type -install -m 644 status %{buildroot}%{_sysconfdir}/e-smith/db/configuration/defaults/%{name}/status -install -m 644 access %{buildroot}%{_sysconfdir}/e-smith/db/configuration/defaults/%{name}/access -install -m 644 TCPPort %{buildroot}%{_sysconfdir}/e-smith/db/configuration/defaults/%{name}/TCPPort +rm -Rf $RPM_BUILD_ROOT +(cd root ; find . -depth -print | cpio -dump %{buildroot}) +rm -f %{name}-%{version}-%{release}-filelist +%{genfilelist} %{buildroot} > %{name}-%{version}-%{release}-filelist +mkdir -p %{buildroot}%{_localstatedir}/log/doraemon +mkdir -p %{buildroot}%{_localstatedir}/cache/doraemon + + +%pre +#if [ "$1" = 1 ]; then +# #installation +#fi +if [ "$1" = 2 ]; then + #upgrade + /sbin/service %{name} stop >/dev/null 2>&1 || : + /sbin/stop %{name} >/dev/null 2>&1 || : + DATABASE=/var/lib/doraemon/doraemon.db + INI=/etc/doraemon.ini + if [ -f $INI ]; then + DATABASE=$(awk -F "=" '/Database/ {print $2}' $INI) + PORT=$(awk -F "=" '/Port/ {print $2}' $INI) + BASE=$(awk -F "=" '/Base/ {print $2}' $INI) + ROLE=$(awk -F "=" '/Role/ {print $2}' $INI) + DIGITS=$(awk -F "=" '/Digits/ {print $2}' $INI) + DOMAIN=$(awk -F "=" '/Domain/ {print $2}' $INI) + MGMTKEY=$(awk -F "=" '/MgmtKey/ {print $2}' $INI) + VAULTPASSFILE=$(awk -F "=" '/VaultPassFile/ {print $2}' $INI) + rm $INI + /sbin/e-smith/db configuration set %{name} service \ + status enabled \ + TCPPort $PORT \ + access private \ + DefaultRole $ROLE \ + DomainFile $DOMAIN \ + ManagementKeyFile $MGMTKEY \ + NamingBase $BASE \ + NamingDigits $DIGITS \ + VaultPassFile $VAULTPASSFILE + else + /sbin/e-smith/db configuration set %{name} service \ + status enabled \ + TCPPort 3000 \ + access private \ + DefaultRole client \ + DomainFile /etc/domain.yml \ + ManagementKeyFile /home/amgmt/.ssh/id_rsa.pub \ + NamingBase lab \ + NamingDigits 2 \ + VaultPassFile /home/amgmt/.ansible/vault.txt + fi + if [ -f $DATABASE ]; then + sqlite3 -separator " " $DATABASE \ + "select * from client" | while read id hostname mac role; do + /sbin/e-smith/db hosts set $hostname local \ + MacAddress $mac Role $role + done + rm $DATABASE + fi + V1=$(/sbin/e-smith/db configuration getprop doraemon ManagementPrivateKeyFile) + if [ "a$V1" == "a" ] ; then + V2=$(/sbin/e-smith/db configuration getprop doraemon ManagementKeyFile | sed "s/\.pub$//") + /sbin/e-smith/db configuration setprop doraemon ManagementPrivateKeyFile "$V2" + fi +fi %post +/etc/e-smith/events/actions/initialize-default-databases +[ -e /usr/sbin/doraemon ] && [ ! -L /usr/sbin/doraemon ] && rm /usr/sbin/doraemon +[ ! -L /usr/sbin/doraemon ] && ln -s httpd /usr/sbin/doraemon if [ "$1" = 1 ]; then - /sbin/chkconfig --add %{name} - /sbin/service %{name} start >/dev/null 2>&1 - /sbin/e-smith/db configuration set %{name} service status enabled TCPPort 3000 access private + #installation + /sbin/e-smith/db configuration set %{name} service status enabled TCPPort 3000 access private DefaultRole client DomainFile /etc/domain.yml ManagementKeyFile /home/amgmt/.ssh/id_rsa.pub NamingBase lab NamingDigits 2 VaultPassFile /home/amgmt/.ansible/vault.txt + /sbin/e-smith/expand-template /etc/sudoers + /sbin/e-smith/expand-template /etc/httpd/doraemon/httpd.conf + /sbin/start %{name} >/dev/null 2>&1 || : /sbin/e-smith/signal-event runlevel-adjust /sbin/e-smith/signal-event firewall-adjust fi +if [ "$1" = 2 ]; then + #upgrade + # TODO: this won't be needed in the next version + /sbin/e-smith/expand-template /etc/sudoers + /sbin/e-smith/expand-template /etc/httpd/doraemon/httpd.conf + /sbin/start %{name} >/dev/null 2>&1 || : + /sbin/e-smith/signal-event runlevel-adjust + /sbin/e-smith/signal-event firewall-adjust +fi + %preun +#if [ "$1" = 1 ]; then +# #upgrade +#fi if [ "$1" = 0 ]; then + #uninstallation + /sbin/stop %{name} >/dev/null 2>&1 || : +fi + + +%postun +#if [ "$1" = 1 ]; then +# #upgrade +#fi +if [ "$1" = 0 ]; then + #uninstallation /sbin/e-smith/db configuration delete %{name} - /sbin/service %{name} stop >/dev/null 2>&1 - chkconfig --del %{name} + /sbin/e-smith/expand-template /etc/sudoers /sbin/e-smith/signal-event runlevel-adjust /sbin/e-smith/signal-event firewall-adjust fi + %clean rm -rf %{buildroot} -%files -%defattr(644,root,root,755) -%doc README.md LICENSE -%config(noreplace) %{_sysconfdir}/%{name}.ini -%attr(755,-,-) %{_initrddir}/%{name} -%attr(755,-,-) %{_bindir}/%{name}.py - -%dir %attr(755,-,-) %{_sharedstatedir}/%{name} -%{_sysconfdir}/logrotate.d/%{name} -%dir %attr(755,-,-) %{_sysconfdir}/e-smith/db/configuration/defaults/%{name} -%{_sysconfdir}/e-smith/db/configuration/defaults/%{name}/status -%{_sysconfdir}/e-smith/db/configuration/defaults/%{name}/type -%{_sysconfdir}/e-smith/db/configuration/defaults/%{name}/access -%{_sysconfdir}/e-smith/db/configuration/defaults/%{name}/TCPPort +%files -f %{name}-%{version}-%{release}-filelist +%defattr(0644,root,root,755) +%doc README.md LICENSE.txt +%attr(0644,srvmgr,srvmgr) %ghost %{_localstatedir}/log/doraemon/access_log +%attr(0644,srvmgr,srvmgr) %ghost %{_localstatedir}/log/doraemon/error_log + %changelog +* Mon Jul 18 2016 Paolo Asperti - 2.0.0-1 +- PHP porting +- Added a basic UI +- Added room support + * Tue Oct 27 2015 Emiliano Vavassori - 1.2.1-2.ns6 - Packing corrections to rpm to fix upgrading issue diff --git a/initrd b/initrd deleted file mode 100644 index 5453b61..0000000 --- a/initrd +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/sh - -# chkconfig: 2345 20 80 -# description: Manage assignation of hostnames for clients -# pidfile: /var/run/doraemon.pid -# config: /etc/doraemon.ini - -### BEGIN INIT INFO -# Provides: doraemon -# Required-Start: $remote_fs $syslog -# Required-Stop: $remote_fs $syslog -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Helps client to join the domain and maintain itself -# Description: Helps client to join the domain and maintain itself -### END INIT INFO - -# Change the next 3 lines to suit where you install your script and what you want to call it -DAEMON=/usr/bin/doraemon.py -DAEMON_NAME=doraemon - -# Add any command line options for your daemon here -DAEMON_OPTS="" - -# This next line determines what user the script runs as. -# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python. -#DAEMON_USER=mac2hostname -#DAEMON_GROUP=mac2hostname - -# The process ID of the script when it runs is stored here: -LOGFILE=/var/log/$DAEMON_NAME.log -PIDFILE=/var/run/$DAEMON_NAME.pid -lockfile=/var/lock/subsys/$DAEMON_NAME - -. /etc/rc.d/init.d/functions -. /etc/sysconfig/network - -[ "$NETWORKING" = "no" ] && exit 0 - -do_start () { - echo -n $"Starting $DAEMON_NAME: " - daemon --pidfile=$PIDFILE "python $DAEMON >>$LOGFILE 2>/dev/null &" - RETVAL=$? - echo - [ $RETVAL -eq 0 ] && touch $lockfile - return $RETVAL -} - -do_stop () { - echo -n $"Stopping $DAEMON_NAME: " - killproc -p $PIDFILE python - RETVAL=$? - echo - [ $RETVAL -eq 0 ] && rm -f $lockfile - return $RETVAL -} - -case "$1" in - - start|stop) - do_${1} - ;; - - restart|reload|force-reload) - do_stop - do_start - ;; - - status) - status -p $PIDFILE python - ;; - - *) - echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}" - exit 1 - ;; - -esac -exit 0 diff --git a/root/etc/ansible/ansible.cfg b/root/etc/ansible/ansible.cfg new file mode 100644 index 0000000..b1a27c0 --- /dev/null +++ b/root/etc/ansible/ansible.cfg @@ -0,0 +1,13 @@ +[defaults] +host_key_checking = False +inventory = /etc/ansible/inventory/ +remote_user = amgmt +private_key_file=/home/amgmt/.ssh/id_rsa +nocows = 1 +log_path = /var/log/ansible.log + +[privilege_escalation] +become=True +become_method=sudo +become_user=root +become_ask_pass=False diff --git a/root/etc/ansible/inventory/clients.pl b/root/etc/ansible/inventory/clients.pl new file mode 100755 index 0000000..1569847 --- /dev/null +++ b/root/etc/ansible/inventory/clients.pl @@ -0,0 +1,38 @@ +#!/usr/bin/perl -w + +package esmith; + +use strict; +use Errno; +use esmith::DB::db; +use IO::File; +use English; +use JSON; + +my $db_hosts = esmith::DB::db->open('hosts') || die("Could not open e-smith db (" . esmith::DB::db->error . ")\n"); +my $db_configuration = esmith::DB::db->open('configuration') || die("Could not open e-smith db (" . esmith::DB::db->error . ")\n"); + +my @hostnames = (); +my %hostnames_map = (); +my %vars = (); + +$vars{"proxy_status"} = $db_configuration->get_prop('squid', 'status'); +$vars{"ansible_user"} = "amgmt"; +$vars{"ansible_ssh_private_key_file"} = $db_configuration->get_prop('doraemon', 'ManagementPrivateKeyFile'); + +my $domainname = $db_configuration->get_prop('DomainName','type'); + +my @items = $db_hosts->get_all('local'); +foreach my $item (@items) { + $hostnames_map{$item} = $item->key . '.' . $domainname; + push @hostnames, $item->key . '.' . $domainname; +} + +my %output = ('clients' => { + 'hosts' => [ @hostnames ], + 'vars' => { %vars } +} ); +my $json = encode_json \%output; +print "$json\n"; + +exit 0; diff --git a/root/etc/ansible/library/e-smith b/root/etc/ansible/library/e-smith new file mode 100755 index 0000000..abfc93f --- /dev/null +++ b/root/etc/ansible/library/e-smith @@ -0,0 +1,24 @@ +#!/usr/bin/perl -w + +package esmith; + +use strict; +use Errno; +use esmith::DB::db; +use IO::File; +use English; +use JSON; + +my $db_configuration = esmith::DB::db->open('configuration') || die("{}"); +my @items = $db_configuration->get_all(); +my %out; +foreach my $item (@items) { + my %props = $item->props(); + if (scalar(keys %props) <= 1 ) { + $out{$item->key} = $props{'type'}; + } else { + $out{$item->key} = { $item->props() }; + } +} + +print encode_json \%out; diff --git a/root/etc/ansible/reconfigure_clients.yml b/root/etc/ansible/reconfigure_clients.yml new file mode 100644 index 0000000..3c1cda4 --- /dev/null +++ b/root/etc/ansible/reconfigure_clients.yml @@ -0,0 +1,8 @@ +--- + +- hosts: clients + user: root + + roles: + - common + - { role: proxy-client, when: proxy_status == 'enabled' } diff --git a/root/etc/ansible/roles/common/tasks/main.yml b/root/etc/ansible/roles/common/tasks/main.yml new file mode 100644 index 0000000..77e5240 --- /dev/null +++ b/root/etc/ansible/roles/common/tasks/main.yml @@ -0,0 +1,34 @@ +--- + +- name: Import e-smith configuration db + e-smith: + register: esmith + delegate_to: localhost + +- name: Set hostname + hostname: name={{inventory_hostname}} + +- name: Update /etc/hosts for localhost + lineinfile: dest=/etc/hosts regexp='^127\.0\.0\.1' line='127.0.0.1 localhost' owner=root group=root mode=0644 + +- name: Update /etc/hosts for hostname + lineinfile: dest=/etc/hosts regexp='^127\.0\.1\.1' line='127.0.1.1 {{inventory_hostname}} {{inventory_hostname_short}}' owner=root group=root mode=0644 + +- name: cron script + template: + src: cronjob + dest: /etc/cron.d/doraemon + force: yes + owner: root + group: root + mode: 0644 + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + +- name: rc.local command to check if apt is pending + blockinfile: + dest: /etc/rc.local + block: | + apt-get check >/dev/null 2>&1 + if [ "$?" -ne 0 ]; then + apt-get -f -y install + fi diff --git a/root/etc/ansible/roles/common/templates/cronjob b/root/etc/ansible/roles/common/templates/cronjob new file mode 100755 index 0000000..ad43848 --- /dev/null +++ b/root/etc/ansible/roles/common/templates/cronjob @@ -0,0 +1,2 @@ +*/5 * * * * root /usr/bin/curl --noproxy '*' "http://{{ esmith.SystemName }}.{{ esmith.DomainName }}:{{ esmith.doraemon.TCPPort }}/ping" +@reboot root /usr/bin/curl --noproxy '*' "http://{{ esmith.SystemName }}.{{ esmith.DomainName }}:{{ esmith.doraemon.TCPPort }}/ping" diff --git a/root/etc/ansible/roles/proxy-client/handlers/main.yml b/root/etc/ansible/roles/proxy-client/handlers/main.yml new file mode 100644 index 0000000..1558b13 --- /dev/null +++ b/root/etc/ansible/roles/proxy-client/handlers/main.yml @@ -0,0 +1,3 @@ +--- +- name: glib-compile-schemas + command: /usr/bin/glib-compile-schemas /usr/share/glib-2.0/schemas/ diff --git a/root/etc/ansible/roles/proxy-client/tasks/apt-proxies.yml b/root/etc/ansible/roles/proxy-client/tasks/apt-proxies.yml new file mode 100644 index 0000000..647d6ab --- /dev/null +++ b/root/etc/ansible/roles/proxy-client/tasks/apt-proxies.yml @@ -0,0 +1,16 @@ +--- + +- name: Set apt proxies + template: + src: 95proxies + dest: /etc/apt/apt.conf.d/95proxies + force: yes + + +- name: proxy gconf schema + template: + src: doraemon-proxy.gschema.override + dest: /usr/share/glib-2.0/schemas/doraemon-proxy.gschema.override + force: yes + notify: + - glib-compile-schemas diff --git a/root/etc/ansible/roles/proxy-client/tasks/main.yml b/root/etc/ansible/roles/proxy-client/tasks/main.yml new file mode 100644 index 0000000..2a2d35f --- /dev/null +++ b/root/etc/ansible/roles/proxy-client/tasks/main.yml @@ -0,0 +1,9 @@ +--- + +- include: apt-proxies.yml + tags: + - proxy-client + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' + + + # when: ansible_distribution == 'CentOS' or ansible_distribution == 'Red Hat Enterprise Linux' diff --git a/root/etc/ansible/roles/proxy-client/templates/95proxies b/root/etc/ansible/roles/proxy-client/templates/95proxies new file mode 100644 index 0000000..935f39e --- /dev/null +++ b/root/etc/ansible/roles/proxy-client/templates/95proxies @@ -0,0 +1,3 @@ +Acquire::http::proxy "http://{{ esmith.SystemName }}.{{ esmith.DomainName }}:3128/"; +Acquire::https::proxy "http://{{ esmith.SystemName }}.{{ esmith.DomainName }}:3128/"; + diff --git a/root/etc/ansible/roles/proxy-client/templates/doraemon-proxy.gschema.override b/root/etc/ansible/roles/proxy-client/templates/doraemon-proxy.gschema.override new file mode 100644 index 0000000..e6b84e4 --- /dev/null +++ b/root/etc/ansible/roles/proxy-client/templates/doraemon-proxy.gschema.override @@ -0,0 +1,11 @@ +[org.gnome.system.proxy] +mode = 'manual' +autoconfig-url = 'http://pdc.mastri.edu/wpad.dat' + +[org.gnome.system.proxy.http] +host = '{{ esmith.SystemName }}.{{ esmith.DomainName }}' +port = 3128 + +[org.gnome.system.proxy.https] +host = '{{ esmith.SystemName }}.{{ esmith.DomainName }}' +port = 3128 diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/DefaultLabID b/root/etc/e-smith/db/configuration/defaults/doraemon/DefaultLabID new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/doraemon/DefaultLabID @@ -0,0 +1 @@ +1 diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/DefaultRole b/root/etc/e-smith/db/configuration/defaults/doraemon/DefaultRole new file mode 100644 index 0000000..b051c6c --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/doraemon/DefaultRole @@ -0,0 +1 @@ +client diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/DomainFile b/root/etc/e-smith/db/configuration/defaults/doraemon/DomainFile new file mode 100644 index 0000000..e978cbd --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/doraemon/DomainFile @@ -0,0 +1 @@ +/etc/domain.yml diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/HostNameFormat b/root/etc/e-smith/db/configuration/defaults/doraemon/HostNameFormat new file mode 100644 index 0000000..52e99e5 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/doraemon/HostNameFormat @@ -0,0 +1 @@ +lab%'.02d-%'.02d diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/ManagementKeyFile b/root/etc/e-smith/db/configuration/defaults/doraemon/ManagementKeyFile new file mode 100644 index 0000000..a2d5166 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/doraemon/ManagementKeyFile @@ -0,0 +1 @@ +/home/amgmt/.ssh/id_rsa.pub diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/ManagementPrivateKeyFile b/root/etc/e-smith/db/configuration/defaults/doraemon/ManagementPrivateKeyFile new file mode 100644 index 0000000..5b4ee9d --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/doraemon/ManagementPrivateKeyFile @@ -0,0 +1 @@ +/home/amgmt/.ssh/id_rsa diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/NamingBase b/root/etc/e-smith/db/configuration/defaults/doraemon/NamingBase new file mode 100644 index 0000000..10441b4 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/doraemon/NamingBase @@ -0,0 +1 @@ +lab diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/NamingDigits b/root/etc/e-smith/db/configuration/defaults/doraemon/NamingDigits new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/doraemon/NamingDigits @@ -0,0 +1 @@ +2 diff --git a/defaults/TCPPort b/root/etc/e-smith/db/configuration/defaults/doraemon/TCPPort similarity index 100% rename from defaults/TCPPort rename to root/etc/e-smith/db/configuration/defaults/doraemon/TCPPort diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/VaultPassFile b/root/etc/e-smith/db/configuration/defaults/doraemon/VaultPassFile new file mode 100644 index 0000000..65f2cd9 --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/doraemon/VaultPassFile @@ -0,0 +1 @@ +/home/amgmt/.ansible/vault.txt diff --git a/root/etc/e-smith/db/configuration/defaults/doraemon/access b/root/etc/e-smith/db/configuration/defaults/doraemon/access new file mode 100644 index 0000000..a48cf0d --- /dev/null +++ b/root/etc/e-smith/db/configuration/defaults/doraemon/access @@ -0,0 +1 @@ +public diff --git a/defaults/status b/root/etc/e-smith/db/configuration/defaults/doraemon/status similarity index 100% rename from defaults/status rename to root/etc/e-smith/db/configuration/defaults/doraemon/status diff --git a/defaults/type b/root/etc/e-smith/db/configuration/defaults/doraemon/type similarity index 100% rename from defaults/type rename to root/etc/e-smith/db/configuration/defaults/doraemon/type diff --git a/root/etc/e-smith/events/actions/doraemon-reboot b/root/etc/e-smith/events/actions/doraemon-reboot new file mode 100755 index 0000000..5f02685 --- /dev/null +++ b/root/etc/e-smith/events/actions/doraemon-reboot @@ -0,0 +1,34 @@ +#!/usr/bin/perl -w + +package esmith; + +use strict; +use Errno; +use esmith::DB::db; +use IO::File; +use English; + +my $db_hosts = esmith::DB::db->open('hosts') || die("Could not open e-smith db (" . esmith::DB::db->error . ")\n"); + +my $event = $ARGV [0]; + +defined $ARGV[1] || die "No host specied"; +my $host = $ARGV[1]; + +reboot_host($host); + +exit 0; + +sub reboot_host +{ + my ($hostName) = @_; + #------------------------------------------------------------ + # Reboot the host + #------------------------------------------------------------ + + my $h = $db_hosts->get($hostName) or die "No host record for user $hostName"; + + system("/usr/bin/sudo -u amgmt /usr/bin/ssh -o StrictHostKeyChecking=no amgmt\@$hostName /usr/bin/sudo /sbin/reboot") == 0 + or die "Error rebooting $hostName"; + +} diff --git a/root/etc/e-smith/events/actions/doraemon-reboot-all b/root/etc/e-smith/events/actions/doraemon-reboot-all new file mode 100755 index 0000000..8036c82 --- /dev/null +++ b/root/etc/e-smith/events/actions/doraemon-reboot-all @@ -0,0 +1,20 @@ +#!/usr/bin/perl -w + +package esmith; + +use strict; +use Errno; +use esmith::DB::db; +use IO::File; +use English; + +my $db_hosts = esmith::DB::db->open('hosts') || die("Could not open e-smith db (" . esmith::DB::db->error . ")\n"); + +my @items = $db_hosts->get_all('local'); +foreach my $item (@items) { + my $hostName = $item->key; + system("/usr/bin/sudo -u amgmt /usr/bin/ssh -o StrictHostKeyChecking=no amgmt\@$hostName /usr/bin/sudo /sbin/reboot &") == 0 + or die "Error rebooting $hostName"; +} + +exit 0; diff --git a/root/etc/e-smith/events/actions/doraemon-reconfigure b/root/etc/e-smith/events/actions/doraemon-reconfigure new file mode 100755 index 0000000..dc42ee7 --- /dev/null +++ b/root/etc/e-smith/events/actions/doraemon-reconfigure @@ -0,0 +1,35 @@ +#!/usr/bin/perl -w + +package esmith; + +use strict; +use Errno; +use esmith::DB::db; +use IO::File; +use English; + +my $db_hosts = esmith::DB::db->open('hosts') || die("Could not open e-smith db (" . esmith::DB::db->error . ")\n"); +my $db_configuration = esmith::DB::db->open('configuration') || die("Could not open e-smith db (" . esmith::DB::db->error . ")\n"); +my $domainname = $db_configuration->get_prop('DomainName','type'); + +my $event = $ARGV [0]; + +defined $ARGV[1] || die "No host specied"; +my $host = $ARGV[1]; + +reconfigure_host($host); + +exit 0; + +sub reconfigure_host +{ + my ($hostName) = @_; + + my $h = $db_hosts->get($hostName) or die "No host record for user $hostName"; + + my $fqdn = $hostName . '.' . $domainname; + + system("/usr/bin/sudo -u amgmt /usr/bin/ansible-playbook -l $fqdn -v /etc/ansible/reconfigure_clients.yml") == 0 + or die "Error reconfiguring $hostName"; + +} diff --git a/root/etc/e-smith/events/actions/doraemon-reconfigure-all b/root/etc/e-smith/events/actions/doraemon-reconfigure-all new file mode 100755 index 0000000..79d11da --- /dev/null +++ b/root/etc/e-smith/events/actions/doraemon-reconfigure-all @@ -0,0 +1,3 @@ +#!/bin/bash + +/usr/bin/sudo -u amgmt /usr/bin/ansible-playbook -v /etc/ansible/reconfigure_clients.yml diff --git a/root/etc/e-smith/events/actions/doraemon-shutdown b/root/etc/e-smith/events/actions/doraemon-shutdown new file mode 100755 index 0000000..04d61c7 --- /dev/null +++ b/root/etc/e-smith/events/actions/doraemon-shutdown @@ -0,0 +1,34 @@ +#!/usr/bin/perl -w + +package esmith; + +use strict; +use Errno; +use esmith::DB::db; +use IO::File; +use English; + +my $db_hosts = esmith::DB::db->open('hosts') || die("Could not open e-smith db (" . esmith::DB::db->error . ")\n"); + +my $event = $ARGV [0]; + +defined $ARGV[1] || die "No host specied"; +my $host = $ARGV[1]; + +shutdown_host($host); + +exit 0; + +sub shutdown_host +{ + my ($hostName) = @_; + #------------------------------------------------------------ + # Shutdown the host + #------------------------------------------------------------ + + my $h = $db_hosts->get($hostName) or die "No host record for user $hostName"; + + system("/usr/bin/sudo -u amgmt /usr/bin/ssh -o StrictHostKeyChecking=no amgmt\@$hostName /usr/bin/sudo /sbin/poweroff") == 0 + or die "Error shutting down $hostName"; + +} diff --git a/root/etc/e-smith/events/actions/doraemon-shutdown-all b/root/etc/e-smith/events/actions/doraemon-shutdown-all new file mode 100755 index 0000000..cd0a5b1 --- /dev/null +++ b/root/etc/e-smith/events/actions/doraemon-shutdown-all @@ -0,0 +1,20 @@ +#!/usr/bin/perl -w + +package esmith; + +use strict; +use Errno; +use esmith::DB::db; +use IO::File; +use English; + +my $db_hosts = esmith::DB::db->open('hosts') || die("Could not open e-smith db (" . esmith::DB::db->error . ")\n"); + +my @items = $db_hosts->get_all('local'); +foreach my $item (@items) { + my $hostName = $item->key; + system("/usr/bin/sudo -u amgmt /usr/bin/ssh -o StrictHostKeyChecking=no amgmt\@$hostName /usr/bin/sudo /sbin/poweroff &") == 0 + or die "Error shutting down $hostName"; +} + +exit 0; diff --git a/root/etc/e-smith/events/actions/doraemon-wake b/root/etc/e-smith/events/actions/doraemon-wake new file mode 100755 index 0000000..bb43cb6 --- /dev/null +++ b/root/etc/e-smith/events/actions/doraemon-wake @@ -0,0 +1,35 @@ +#!/usr/bin/perl -w + +package esmith; + +use strict; +use Errno; +use esmith::DB::db; +use IO::File; +use English; + +my $db_hosts = esmith::DB::db->open('hosts') || die("Could not open e-smith db (" . esmith::DB::db->error . ")\n"); + +my $event = $ARGV [0]; + +defined $ARGV[1] || die "No host specied"; +my $host = $ARGV[1]; + +wake_host($host); + +exit 0; + +sub wake_host +{ + my ($hostName) = @_; + #------------------------------------------------------------ + # Wake up the host + #------------------------------------------------------------ + + my $h = $db_hosts->get($hostName) or die "No host record for user $hostName"; + my $client = $h[0]; + my $mac = $client->prop('MacAddress'); + system("/sbin/ether-wake -i lan0 $mac") == 0 + or die "Error waking up $hostName"; + +} diff --git a/root/etc/e-smith/events/actions/doraemon-wake-all b/root/etc/e-smith/events/actions/doraemon-wake-all new file mode 100755 index 0000000..274d231 --- /dev/null +++ b/root/etc/e-smith/events/actions/doraemon-wake-all @@ -0,0 +1,21 @@ +#!/usr/bin/perl -w + +package esmith; + +use strict; +use Errno; +use esmith::DB::db; +use IO::File; +use English; + +my $db_hosts = esmith::DB::db->open('hosts') || die("Could not open e-smith db (" . esmith::DB::db->error . ")\n"); + +my @items = $db_hosts->get_all('local'); +foreach my $item (@items) { + my $hostName = $item->key; + my $mac = $item->prop('MacAddress'); + system("/sbin/ether-wake -i lan0 $mac &") == 0 + or die "Error waking up $hostName"; +} + +exit 0; diff --git a/root/etc/e-smith/events/doraemon-reboot-all/S50doraemon-reboot-all b/root/etc/e-smith/events/doraemon-reboot-all/S50doraemon-reboot-all new file mode 120000 index 0000000..d03ca78 --- /dev/null +++ b/root/etc/e-smith/events/doraemon-reboot-all/S50doraemon-reboot-all @@ -0,0 +1 @@ +../actions/doraemon-reboot-all \ No newline at end of file diff --git a/root/etc/e-smith/events/doraemon-reboot/S50doraemon-reboot b/root/etc/e-smith/events/doraemon-reboot/S50doraemon-reboot new file mode 120000 index 0000000..42198ce --- /dev/null +++ b/root/etc/e-smith/events/doraemon-reboot/S50doraemon-reboot @@ -0,0 +1 @@ +../actions/doraemon-reboot \ No newline at end of file diff --git a/root/etc/e-smith/events/doraemon-reconfigure-all/S50doraemon-reconfigure-all b/root/etc/e-smith/events/doraemon-reconfigure-all/S50doraemon-reconfigure-all new file mode 120000 index 0000000..6f54f17 --- /dev/null +++ b/root/etc/e-smith/events/doraemon-reconfigure-all/S50doraemon-reconfigure-all @@ -0,0 +1 @@ +../actions/doraemon-reconfigure-all \ No newline at end of file diff --git a/root/etc/e-smith/events/doraemon-reconfigure/S50doraemon-reconfigure b/root/etc/e-smith/events/doraemon-reconfigure/S50doraemon-reconfigure new file mode 120000 index 0000000..c8febb7 --- /dev/null +++ b/root/etc/e-smith/events/doraemon-reconfigure/S50doraemon-reconfigure @@ -0,0 +1 @@ +../actions/doraemon-reconfigure \ No newline at end of file diff --git a/root/etc/e-smith/events/doraemon-shutdown-all/S50doraemon-shutdown-all b/root/etc/e-smith/events/doraemon-shutdown-all/S50doraemon-shutdown-all new file mode 120000 index 0000000..c2cdf5f --- /dev/null +++ b/root/etc/e-smith/events/doraemon-shutdown-all/S50doraemon-shutdown-all @@ -0,0 +1 @@ +../actions/doraemon-shutdown-all \ No newline at end of file diff --git a/root/etc/e-smith/events/doraemon-shutdown/S50doraemon-shutdown b/root/etc/e-smith/events/doraemon-shutdown/S50doraemon-shutdown new file mode 120000 index 0000000..91b5632 --- /dev/null +++ b/root/etc/e-smith/events/doraemon-shutdown/S50doraemon-shutdown @@ -0,0 +1 @@ +../actions/doraemon-shutdown \ No newline at end of file diff --git a/root/etc/e-smith/events/doraemon-wake-all/S50doraemon-wake-all b/root/etc/e-smith/events/doraemon-wake-all/S50doraemon-wake-all new file mode 120000 index 0000000..6642a73 --- /dev/null +++ b/root/etc/e-smith/events/doraemon-wake-all/S50doraemon-wake-all @@ -0,0 +1 @@ +../actions/doraemon-wake-all \ No newline at end of file diff --git a/root/etc/e-smith/events/doraemon-wake/S50doraemon-wake b/root/etc/e-smith/events/doraemon-wake/S50doraemon-wake new file mode 120000 index 0000000..c8520f0 --- /dev/null +++ b/root/etc/e-smith/events/doraemon-wake/S50doraemon-wake @@ -0,0 +1 @@ +../actions/doraemon-wake \ No newline at end of file diff --git a/root/etc/e-smith/templates.metadata/etc/init/doraemon.conf b/root/etc/e-smith/templates.metadata/etc/init/doraemon.conf new file mode 100644 index 0000000..5e5de6f --- /dev/null +++ b/root/etc/e-smith/templates.metadata/etc/init/doraemon.conf @@ -0,0 +1,3 @@ +TEMPLATE_PATH='upstart-job' +OUTPUT_FILENAME='/etc/init/doraemon.conf' +MORE_DATA={ name => 'doraemon', description => 'Apache instance that runs the doraemon service', author => 'Paolo Asperti ', daemon_bin => '/usr/sbin/doraemon', daemon_args => '-f /etc/httpd/doraemon/httpd.conf', respawn => 1, 'expect' => 'fork', stop_on => 'stopping network', start_on => 'started network' } diff --git a/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/00template_defaults b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/00template_defaults new file mode 100644 index 0000000..4c1a258 --- /dev/null +++ b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/00template_defaults @@ -0,0 +1,10 @@ +{ + # + # 00template_defaults + # + + $localAccess = ''; + $port = ${'doraemon'}{TCPPort} || "3000"; + + ""; +} \ No newline at end of file diff --git a/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/20Manager b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/20Manager new file mode 100644 index 0000000..b879b6a --- /dev/null +++ b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/20Manager @@ -0,0 +1,127 @@ +{ + + $OUT .= <s %b" common +LogFormat "%{User-agent}i" agent + +CustomLog /var/log/doraemon/access_log common + +KeepAlive On +MaxKeepAliveRequests 100 +KeepAliveTimeout 15 + +MaxClients 150 +MaxRequestsPerChild 100 + +MinSpareServers 1 +MaxSpareServers 5 +StartServers 1 +Timeout 300 + +DefaultIcon /icons/unknown.gif +DirectoryIndex index.htm index.html index.shtml index.cgi +IndexOptions FancyIndexing VersionSort NameWidth=* +IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t +AccessFileName .htaccess + +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* +DefaultType text/plain +TypesConfig /etc/mime.types + +AddEncoding x-compress Z +AddEncoding x-gzip gz + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +AddIcon /icons/bomb.gif core + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +AddLanguage en .en +AddLanguage fr .fr +AddLanguage de .de +AddLanguage da .da +AddLanguage el .el +AddLanguage it .it + +LanguagePriority en it de fr + +AddType text/html .shtml +AddType application/x-pkcs7-crl .crl + +AddType application/x-x509-ca-cert .crt + +BrowserMatch "Mozilla/2" nokeepalive +BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 +BrowserMatch "RealPlayer 4\.0" force-response-1.0 +BrowserMatch "Java/1\.0" force-response-1.0 +BrowserMatch "JDK/1\.0" force-response-1.0 + +AddHandler cgi-script .cgi +AddHandler server-parsed .shtml +AddHandler imap-file map + +HERE +} + diff --git a/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/40DefaultAccess b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/40DefaultAccess new file mode 100644 index 0000000..fe3528e --- /dev/null +++ b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/40DefaultAccess @@ -0,0 +1,16 @@ + +# First, we configure the "default" to be a very restrictive set of +# permissions. + + + Options None + AllowOverride None + order deny,allow + deny from all + allow from none + + + + Allow from all + + diff --git a/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/60DoraemonInterface b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/60DoraemonInterface new file mode 100644 index 0000000..743bbf3 --- /dev/null +++ b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/60DoraemonInterface @@ -0,0 +1,23 @@ + +DocumentRoot /usr/share/doraemon +Alias /icons/ /var/www/icons/ + + + Options +Indexes + Order Deny,Allow + Deny from all + Allow from { $localAccess || 'all' } + AddType application/x-httpd-php .php + Options +FollowSymlinks + RewriteEngine on + RewriteBase / + RewriteRule ^(js/|fonts/|css/|images/|icons/|.+\.php) - [PT,L] + RewriteRule ^favicon.ico$ images/favicon.ico [L] + RewriteRule ^(.*)$ index.php/$1 [L] + php_value session.save_path /var/cache/doraemon + php_flag magic_quotes_gpc off + + + + + diff --git a/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/90include b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/90include new file mode 100644 index 0000000..e7f3cdb --- /dev/null +++ b/root/etc/e-smith/templates/etc/httpd/doraemon/httpd.conf/90include @@ -0,0 +1 @@ +Include conf.d/php.conf diff --git a/root/etc/e-smith/templates/etc/sudoers/50doraemon b/root/etc/e-smith/templates/etc/sudoers/50doraemon new file mode 100644 index 0000000..06692c8 --- /dev/null +++ b/root/etc/e-smith/templates/etc/sudoers/50doraemon @@ -0,0 +1,15 @@ +{ + $OUT = 'srvmgr ALL=NOPASSWD: /bin/cat '; + + use esmith::ConfigDB; + my $confdb = esmith::ConfigDB->open; + my $backup = $confdb->get('doraemon') or die "No doraemon db entry found\n"; + my $VaultPassFile = $backup->prop('VaultPassFile') || '/home/amgmt/.ansible/vault.txt'; + my $ManagementKeyFile = $backup->prop('ManagementKeyFile') || '/home/amgmt/.ssh/id_rsa.pub'; + my $DomainFile = $backup->prop('DomainFile') || '/etc/domain.yml'; + + $OUT = 'srvmgr ALL=NOPASSWD: /bin/cat ' . $VaultPassFile . ' +srvmgr ALL=NOPASSWD: /bin/cat ' . $ManagementKeyFile .' +srvmgr ALL=NOPASSWD: /bin/cat ' . $DomainFile; + +} diff --git a/root/etc/init/doraemon-reload.conf b/root/etc/init/doraemon-reload.conf new file mode 100644 index 0000000..be8a4b0 --- /dev/null +++ b/root/etc/init/doraemon-reload.conf @@ -0,0 +1,12 @@ +# +# doraemon-reload task +# +# Signals an Apache graceful restart for doraemon instance +# + +# service job, runs asynchronously + +script + sleep 5 + exec /usr/sbin/doraemon -f /etc/httpd/doraemon/httpd.conf -k graceful +end script diff --git a/root/etc/init/doraemon.conf b/root/etc/init/doraemon.conf new file mode 100644 index 0000000..e091ef8 --- /dev/null +++ b/root/etc/init/doraemon.conf @@ -0,0 +1,42 @@ +# ================= DO NOT MODIFY THIS FILE ================= +# +# Manual changes will be lost when this file is regenerated. +# +# Please read the developer's guide, which is available +# at https://dev.nethesis.it/projects/nethserver/wiki/NethServer +# original work from http://www.contribs.org/development/ +# +# Copyright (C) 2013 Nethesis S.r.l. +# http://www.nethesis.it - support@nethesis.it +# +# +# 10base doraemon - Apache instance that runs the doraemon service +# + +description "Apache instance that runs the doraemon service" +author "Paolo Asperti " +respawn +respawn limit 10 5 +expect fork +start on started network +stop on stopping network + + +# +# 20pre_start +# +pre-start script + STAT=`/sbin/e-smith/config getprop doraemon status` + if test "x$STAT" = "xenabled" \ + -a -f "/etc/e-smith/db/configuration/defaults/doraemon/status" \ + -a -x /usr/sbin/doraemon; then + exit 0; + fi + stop; exit 0; +end script + + +# +# 30script +# +exec /usr/sbin/doraemon -f /etc/httpd/doraemon/httpd.conf diff --git a/root/etc/logrotate.d/doraemon b/root/etc/logrotate.d/doraemon new file mode 100644 index 0000000..d409c39 --- /dev/null +++ b/root/etc/logrotate.d/doraemon @@ -0,0 +1,9 @@ +/var/log/doraemon/*log { + missingok + notifempty + sharedscripts + delaycompress + postrotate + /sbin/reload doraemon > /dev/null 2>/dev/null || true + endscript +} \ No newline at end of file diff --git a/root/usr/share/doraemon/EsmithDatabase.php b/root/usr/share/doraemon/EsmithDatabase.php new file mode 100644 index 0000000..e0e0268 --- /dev/null +++ b/root/usr/share/doraemon/EsmithDatabase.php @@ -0,0 +1,391 @@ +. + */ + +/** + * Read and write parameters into SME DB + * + * Ths class implements an interface to SME database executing the command /sbin/e-smith/db with sudo. + * The class needs /etc/sudoers configurazione. In the sudoers file you must have something like this: + * + * Cmnd_Alias SME = /sbin/e-smith/db, /sbin/e-smith/signal-event + * www ALL=NOPASSWD: SME + * + * + * + * @author Giacomo Sanchietti + * @author Davide Principi + * @since 1.0 + * @internal + */ +class EsmithDatabase +{ + + /** + * @var SME DB database command + * */ + private $command = "/usr/bin/sudo /sbin/e-smith/db"; + + /** + * @var $db Database name, it's translated into the db file path. For example: /home/e-smith/db/testdb + * */ + private $db; + + /** + * This static class variable shares a socket connection to smwingsd + * + * @var resource + */ + private static $socket; + + + + + /** + * Construct an object to access a SME Configuration database file + * with $user's privileges. + * + * @param string $database Database name + */ + public function __construct($database) + { + if ( ! $database) + sprintf("%s: You must provide a valid database name.", get_class($this)); + + $this->db = $database; + } + + public function getAll($type = NULL) + { + $output = ""; + + $ret = $this->dbRead('getjson', array(), $output); + if ($ret !== 0) { + sprintf("%s: internal database command failed!", __CLASS__); + } + + $data = json_decode($output, TRUE); + if ( ! is_array($data)) { + sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8)); + } + + $result = array(); + + foreach ($data as $item) { + // Apply type check filter: + if (isset($type) && $type !== $item['type']) { + continue; + } + $props = isset($item['props']) ? $item['props'] : array(); + $result[$item['name']] = array_merge($props, array('type' => $item['type'])); + } + + return $result; + } + + public function getAllByProp($propName = NULL, $propValue = NULL) + { + $output = ""; + + $ret = $this->dbRead('getjson', array(), $output); + if ($ret !== 0) { + sprintf("%s: internal database command failed!", __CLASS__); + } + + $data = json_decode($output, TRUE); + if ( ! is_array($data)) { + sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8)); + } + + $result = array(); + + foreach ($data as $item) { + $item['props']['type'] = $item['type']; + $item['props']['name'] = $item['name']; + + if (isset($propName) && isset($propValue)) { + if (isset($item['props'][$propName]) && $propValue == $item['props'][$propName]) { + $result[$item['name']] = $item['props']; + } + } else { + $result[$item['name']] = $item['props']; + } + } + + return $result; + } + + public function getKey($key) + { + $output = ''; + + $ret = $this->dbRead('getjson', array($key), $output); + if ($ret !== 0) { + sprintf("%s: internal database command failed", __CLASS__); + } + + $data = json_decode($output, TRUE); + + if ($data === 1) { + // Key has not been found + return array(); + } elseif ( ! is_array($data)) { + sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8)); + } + + $data['props']['type'] = $data['type']; + $data['props']['name'] = $data['name']; + return $data['props']; + } + + public function getKeyValue($key) + { + $output = ''; + + $ret = $this->dbRead('getjson', array($key), $output); + if ($ret !== 0) { + sprintf("%s: internal database command failed", __CLASS__); + } + + $data = json_decode($output, TRUE); + + if ($data === 1) { + // Key has not been found + return array(); + } elseif ( ! is_array($data)) { + sprintf("%s: unexpected json string `%s`", __CLASS__, substr($output, 0, 8)); + } + + return $data['type']; + } + + public function setKey($key, $type, $props) + { + $output = NULL; + $ret = $this->dbExec('set', $this->prepareArguments($key, $type, $props), $output); + return ($ret == 0); + } + + public function deleteKey($key) + { + $output = NULL; + $output = NULL; + $ret = $this->dbExec('delete', $this->prepareArguments($key), $output); + return ($ret == 0); + } + + /** + * Return the type of a key + * Act like: /sbin/e-smith/db dbfile gettype key + * + * @param string $key the key to retrieve + * @access public + * @return string the type of the key + */ + public function getType($key) + { + $output = NULL; + $ret = $this->dbRead('gettype', array($key), $output); + return trim($output); + } + + /** + * Set the type of a key + * Act like: /sbin/e-smith/db dbfile settype key type + * + * @param string $key the key to change + * @param string $type the new type + * @access public + * @return bool true on success, FALSE otherwise + */ + public function setType($key, $type) + { + $output = NULL; + $ret = $this->dbExec('settype', $this->prepareArguments($key, $type), $output); + return ($ret == 0); + } + + public function getProp($key, $prop) + { + $output = NULL; + $ret = $this->dbRead('getprop', array($key, $prop), $output); + return trim($output); + } + + public function setProp($key, $props) + { + $output = NULL; + $ret = $this->dbExec('setprop', $this->prepareArguments($key, $props), $output); + return ($ret == 0); + } + + public function delProp($key, $props) + { + $output = NULL; + $ret = $this->dbExec('delprop', array_merge(array($key), array_values($props)), $output); + return ($ret == 0); + } + + /** + * Execute the db command + * @param string $command The command to invoke + * @param array $args The command arguments + * @param string &$output The output from the command process + * @return int The command exit code + */ + private function dbExec($command, $args, &$output) + { + // prepend the database name and command + array_unshift($args, $this->db, $command); + $exitCode = 0; + $oArr = array(); + exec($this->command . ' ' . implode(' ', array_map('escapeshellarg', $args)), $oArr, $exitCode); + $output = implode("\n", $oArr); + return $exitCode; + } + + /** + * Read db data from memory cache daemon + * @param string $command The command to invoke + * @param array $args The command arguments + * @param string &$output The output from the read socket + * @return int The command exit code + */ + private function dbRead($command, $args, &$output) + { + if ( ! isset(self::$socket) ) { + $socketPath = '/var/run/smwingsd.sock'; + $errno = 0; + $errstr = ''; + self::$socket = fsockopen('unix://' . $socketPath, -1, $errno, $errstr); + if( ! is_resource(self::$socket)) { + sprintf("Invalid socket (%d): %s. Fall back to exec().", $errno, $errstr); + } + } + + if ( ! is_resource(self::$socket)) { + return $this->dbExec($command, call_user_func_array(array($this, 'prepareArguments'), $args), $output); + } + + // prepend the database name and command + array_unshift($args, $this->db, $command); + + $ret = $this->sendMessage(self::$socket, 0x10, $args); + if ( ! $ret ) { + return 1; + } + $ret = $this->recvMessage(self::$socket); + if ( ! $ret ) { + return 1; + } + if ($command === 'getjson') { + $output = $ret; + } else { + $output = json_decode($ret, TRUE); + } + + return 0; + } + + private function sendMessage($socket, $type, $args = array()) + { + $payload = json_encode($args); + $data = pack('CN', (int) $type, strlen($payload)) . $payload; + $written = fwrite($socket, $data); + if ($written !== strlen($data)) { + echo 'Socket write error'; + } + return TRUE; + } + + private function recvMessage($socket) + { + $buf = $this->safeRead($socket, 5); + if ($buf === FALSE) { + echo 'Socket read error'; + return false; + } + + $header = unpack('Ctype/Nsize', $buf); + if ( ! is_array($header)) { + echo 'Invalid message header'; + return false; + } + + $message = $this->safeRead($socket, $header['size']); + if ($message === FALSE) { + echo 'Socket read error'; + return false; + } + + if ($header['type'] & 0x02) { + return NULL; + } + + return $message; + } + + private function safeRead($socket, $size) + { + $buffer = ""; + $count = 0; + while($count < $size) { + if(feof($socket)) { + return FALSE; + } + $chunk = fread($socket, $size - $count); + $count += strlen($chunk); + if($chunk === FALSE) { + return FALSE; + } + $buffer .= $chunk; + } + return $buffer; + } + + + /** + * Take arbitrary arguments and flattenize to an array + * + * @param mixed $_ + * @return array + */ + private function prepareArguments() + { + $args = array(); + + foreach (func_get_args() as $arg) { + if (is_array($arg)) { + foreach ($arg as $propName => $propValue) { + $args[] = $propName; + $args[] = $propValue; + } + } else { + $args[] = (String) $arg; + } + } + + return $args; + } + + + + +} \ No newline at end of file diff --git a/root/usr/share/doraemon/index.php b/root/usr/share/doraemon/index.php new file mode 100644 index 0000000..ce2c6a9 --- /dev/null +++ b/root/usr/share/doraemon/index.php @@ -0,0 +1,326 @@ +getKey($role); + $return = array('role'=>$role,'delpkg'=>'','addpkg'=>''); + if (is_array($result)) { + if (is_string($result['Addpkg'])) + $return['addpkg'] = preg_split("/[\s,]+/", $result['Addpkg']); + if (is_string($result['Delpkg'])) + $return['delpkg'] = preg_split("/[\s,]+/", $result['Delpkg']); + } + if ($json) { + return json_encode($return); + } else { + return $return; + } +} + +function getClient($mac=false) { + global $hosts_db; + if (!$mac) { + $mac = getClientMac(); + if (!$mac) return false; + } + $hosts = $hosts_db->getAllByProp('MacAddress',$mac); + if (count($hosts)>0) { + return array_pop($hosts); + } else { + return false; + } +} + +function getClientMac($ip=false) { + if (false === $ip) { + $ip = $_SERVER['REMOTE_ADDR']; + } + $result = pingHost($ip,1); + if (!$result) return false; + $macAddr=false; + $arptable=`arp -n $ip`; + $lines=explode("\n", $arptable); + foreach($lines as $line) { + $cols=preg_split('/\s+/', trim($line)); + if ($cols[0]==$ip) + { + return $cols[2]; + } + } +} + +function newHostname($mac, /*$base,*/ $role, $labid) { + global $config_db; + global $hosts_db; + if(!preg_match('/^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/', $mac)) { + echo "invalid mac address: $mac"; + return; + } + + if ($client=getClient($mac)) { + echo $client['name']; + return; + } + + $domainName = (string)$config_db->getKeyValue('DomainName'); + //$digits = $config_db->getProp(CONFIG_KEY,'NamingDigits'); + //$formatstring = '%s-%0'.$digits.'d'; + $formatstring = $config_db->getKeyValue('HostNameFormat'); + $nr=0; + $hostname = ''; + do { + $nr++; + $hostname = sprintf($formatstring, $labid, $nr); +# TODO: we'll use the hostname in future, maybe +# if (trim($domainName) != '') { +# $hostname .= '.' . $domainName; +# } + } while ($hosts_db->getKey($hostname)); + + $hosts_db->setKey($hostname, 'local', array( + 'MacAddress' => $mac, + 'Role' => $role, + 'Description' => '', + 'LabID' => $labid + )); + + signalEvent('doraemon-reconfigure', $hostname); + + echo $hostname; +} + +function signalEvent($eventSpecification, $arguments = array()) +{ + $cmd = array( + escapeshellcmd('/usr/bin/sudo -n /sbin/e-smith/signal-event'), + escapeshellarg($eventSpecification) + ); + if (is_array($arguments)) { + $a = array_map('escapeshellarg', $arguments); + $cmd = array_merge($cmd, $a); + } else { + $cmd[] = escapeshellarg($arguments); + } + + exec(join(' ',$cmd)); + return true; +} + +function pingHost($host, $timeout=1) { + $pingresult = exec("/bin/ping -c 1 -t 2 -W $timeout $host", $output, $retvar); + return (0 == $retvar); +} + +function ROUTE_reconfigureme() { + global $config_db; + $mac = getClientMac(); + if (!$mac) { + echo 'no-mac-address'; + return false; + } + if (!$client=getClient($mac)) { + echo 'unknown client'; + return false; + } + $hostname = $client['name']; + signalEvent('doraemon-reconfigure', $hostname); + echo "$hostname \n"; +} + +function ROUTE_ping() { + // TODO: controllare ultimo aggiornamento + ROUTE_reconfigureme(); +} + +function ROUTE_domain() { + global $config_db; + $theFile = $config_db->getProp(CONFIG_KEY,'DomainFile'); + passthru("/usr/bin/sudo /bin/cat $theFile"); +} + +function ROUTE_mgmtkey() { + global $config_db; + $theFile = $config_db->getProp(CONFIG_KEY,'ManagementKeyFile'); + passthru("/usr/bin/sudo /bin/cat $theFile"); +} + +function ROUTE_epoptes_srv() { + global $hosts_db; + global $get_params; + global $config_db; + + if (isset($get_params['labid'])) { + $labid = $get_params['labid']; + } else { + $labid = $config_db->getProp(CONFIG_KEY,'DefaultLabID'); + } + + $hosts = $hosts_db->getAllByProp('Role','docenti'); + + $hostsFiltered = array_filter( + $hosts, + function ($item) use ($labid) { + return $item['labid'] == $labid; + } + ); + + if (count($hostsFiltered) == 0) { + echo 'none'; + } else { + $host = array_pop($hostsFiltered); + echo $host['name']; + } +} + +function ROUTE_ansible_host() { + global $get_params; + if (isset($get_params['role'])) { + $role = $get_params['role']; + } elseif ($client = getClient()) { + $role = $client['Role']; + } else { + $role = 'unknown'; + } + echo varsForRole($role); +} + +/** + * @deprecated + * Will be removed!!! + */ +function ROUTE_hosts() { + global $hosts_db; + global $get_params; + if (isset($get_params['role'])) { + $items = $hosts_db->getAllByProp('Role', $get_params['role']); + } else { + $items = $hosts_db->getAll('local'); + } + $results=array(); + foreach ($items as $name=>$item) { + $results[]=array( + 'mac'=>$item['MacAddress'], + 'hostname'=>$name, + 'role'=>$item['Role'] + ); + } + echo json_encode($results); +} + +/** + * @deprecated + * Will be removed!!! + */ +function ROUTE_ansible_list() { + global $get_params; + if (isset($get_params['role'])) { + $role = $get_params['role']; + } elseif ($client = getClient()) { + $role = $client['Role']; + } else { + $role = 'unknown'; + } + $vars = varsForRole($role, false); + echo json_encode(array( + 'localhost'=>array( + 'hosts'=>array('localhost'), + 'vars'=>array('ansible_connection'=>'local') + ), + '_meta'=> array('hostvars'=>array('localhost'=>$vars)) + )); +} + +function ROUTE_vaultpass() { + global $config_db; + $theFile = $config_db->getProp(CONFIG_KEY,'VaultPassFile'); + $oArr = array(); + exec('/usr/bin/sudo /bin/cat ' . $theFile, $oArr, $exitCode); + $content = trim(implode("\n", $oArr)); + if (false === $content) { + echo 'no file'; return; + } + if (!$client = getClient()) { + echo 'no client'; return; + } + $key = md5($client['name'], true); + $padlenght = 16 - (strlen($content) % 16); + $content_padded = $content . str_repeat('x', $padlenght); + $ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $content_padded, MCRYPT_MODE_ECB); + echo base64_encode($ciphertext); +} + +function ROUTE_whatsmyhostname() { + global $config_db; + $mac = getClientMac(); + if (!$mac) { + echo 'no-mac-address'; + return false; + } + /*$base = $config_db->getProp(CONFIG_KEY,'NamingBase');*/ + $role = $config_db->getProp(CONFIG_KEY,'DefaultRole'); + $labid = $config_db->getProp(CONFIG_KEY,'DefaultLabID'); + + newHostname($mac, /*$base,*/ $role, $labid); +} + +function ROUTE_mac2hostname() { + global $config_db; + global $get_params; + $mac = getClientMac(); + if (!$mac) { + echo 'Usage: GET /mac2hostname?mac=XX_XX_XX_XX_XX_XX[&base=YYY][&role=ZZZ][&labid=WW]'; + return; + } + + if (isset($get_params['role'])) { + $role = $get_params['role']; + } else { + $role = $config_db->getProp(CONFIG_KEY,'DefaultRole'); + } + +// if (isset($get_params['base'])) { +// $base = $get_params['base']; +// } else { +// $base = $config_db->getProp(CONFIG_KEY,'NamingBase'); +// } + + if (isset($get_params['labid'])) { + $labid = $get_params['labid']; + } else { + $labid = $config_db->getProp(CONFIG_KEY,'DefaultLabID'); + } + + newHostname($mac, /*$base,*/ $role, $labid); +} diff --git a/root/usr/share/nethesis/NethServer/Language/en/NethServer_Module_Doraemon.php b/root/usr/share/nethesis/NethServer/Language/en/NethServer_Module_Doraemon.php new file mode 100644 index 0000000..6f87ce3 --- /dev/null +++ b/root/usr/share/nethesis/NethServer/Language/en/NethServer_Module_Doraemon.php @@ -0,0 +1,13 @@ +loadChildrenDirectory(); + } + +} diff --git a/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts.php b/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts.php new file mode 100644 index 0000000..2e0be62 --- /dev/null +++ b/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts.php @@ -0,0 +1,37 @@ +setTableAdapter($this->getPlatform()->getTableAdapter('hosts', 'local')) + ->setColumns($columns) + ->addRowAction(new \NethServer\Module\Doraemon\Hosts\Modify('update')) + ->addRowAction(new \NethServer\Module\Doraemon\Hosts\TogglePower('wake')) + ->addRowAction(new \NethServer\Module\Doraemon\Hosts\TogglePower('reboot')) + ->addRowAction(new \NethServer\Module\Doraemon\Hosts\TogglePower('shutdown')) + ->addRowAction(new \NethServer\Module\Doraemon\Hosts\Modify('delete')) + ->addTableAction(new \NethServer\Module\Doraemon\Hosts\Modify('create')) + ->addTableAction(new \NethServer\Module\Doraemon\Hosts\TogglePowerAll('wake-all')) + ->addTableAction(new \NethServer\Module\Doraemon\Hosts\TogglePowerAll('reboot-all')) + ->addTableAction(new \NethServer\Module\Doraemon\Hosts\TogglePowerAll('shutdown-all')) + ->addTableAction(new \Nethgui\Controller\Table\Help('Help')) + ; + + parent::initialize(); + } + +} diff --git a/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts/Modify.php b/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts/Modify.php new file mode 100644 index 0000000..a6f228e --- /dev/null +++ b/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts/Modify.php @@ -0,0 +1,56 @@ +setSchema($parameterSchema); + parent::initialize(); + } + + public function validate(\Nethgui\Controller\ValidationReportInterface $report) + { + if ($this->getIdentifier() === 'delete') { + $v = $this->createValidator()->platform('host-delete'); + if ( ! $v->evaluate($this->parameters['hostname'])) { + $report->addValidationError($this, 'Key', $v); + } + } + parent::validate($report); + } + + protected function onParametersSaved($parameters) + { + $actionName = $this->getIdentifier(); + if ($actionName === 'update') { + $actionName = 'modify'; + } + $this->getPlatform()->signalEvent(sprintf('host-%s &', $actionName)); + } + + public function prepareView(\Nethgui\View\ViewInterface $view) + { + parent::prepareView($view); + $templates = array( + 'create' => 'NethServer\Template\Doraemon\Hosts', + 'update' => 'NethServer\Template\Doraemon\Hosts', + 'delete' => 'Nethgui\Template\Table\Delete', + ); + $view->setTemplate($templates[$this->getIdentifier()]); + } + +} diff --git a/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts/TogglePower.php b/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts/TogglePower.php new file mode 100644 index 0000000..b9a366b --- /dev/null +++ b/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts/TogglePower.php @@ -0,0 +1,48 @@ +declareParameter('hostname', Validate::HOSTNAME_SIMPLE); + + parent::bind($request); + $hostname = \Nethgui\array_end($request->getPath()); + + if ( ! $hostname) { + throw new \Nethgui\Exception\HttpException('Not found', 404, 1322148400); + } + + $this->parameters['hostname'] = $hostname; + } + + public function process() + { + if ( ! $this->getRequest()->isMutation()) { + return; + } + + $this->getPlatform()->signalEvent(sprintf('doraemon-%s@post', $this->getIdentifier()), array($this->parameters['hostname'])); + } + +} diff --git a/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts/TogglePowerAll.php b/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts/TogglePowerAll.php new file mode 100644 index 0000000..b1c7a26 --- /dev/null +++ b/root/usr/share/nethesis/NethServer/Module/Doraemon/Hosts/TogglePowerAll.php @@ -0,0 +1,34 @@ +getRequest()->isMutation()) { + return; + } + + $this->getPlatform()->signalEvent(sprintf('doraemon-%s@post', $this->getIdentifier())); + } + +} diff --git a/root/usr/share/nethesis/NethServer/Module/Doraemon/Rooms.php b/root/usr/share/nethesis/NethServer/Module/Doraemon/Rooms.php new file mode 100644 index 0000000..6859baf --- /dev/null +++ b/root/usr/share/nethesis/NethServer/Module/Doraemon/Rooms.php @@ -0,0 +1,28 @@ +setTableAdapter($this->getPlatform()->getTableAdapter('rooms', 'room')) + ->setColumns($columns) + ->addRowAction(new \NethServer\Module\Doraemon\Rooms\Modify('update')) + ->addRowAction(new \NethServer\Module\Doraemon\Rooms\Modify('delete')) + ->addTableAction(new \NethServer\Module\Doraemon\Rooms\Modify('create')) + ->addTableAction(new \Nethgui\Controller\Table\Help('Help')) + ; + + parent::initialize(); + } + +} diff --git a/root/usr/share/nethesis/NethServer/Module/Doraemon/Rooms/Modify.php b/root/usr/share/nethesis/NethServer/Module/Doraemon/Rooms/Modify.php new file mode 100644 index 0000000..6ee5e48 --- /dev/null +++ b/root/usr/share/nethesis/NethServer/Module/Doraemon/Rooms/Modify.php @@ -0,0 +1,89 @@ +setSchema($parameterSchema); + parent::initialize(); + } + + public function bind(\Nethgui\Controller\RequestInterface $request) + { + parent::bind($request); + if($request->isMutation()) { + // save the old values for later usage: + $this->originalAclRead = $this->getPlatform()->getDatabase('accounts')->getProp($this->parameters['ibay'], 'AclRead'); + $this->originalAclWrite = $this->getPlatform()->getDatabase('accounts')->getProp($this->parameters['ibay'], 'AclWrite'); + } + } + + public function validate(\Nethgui\Controller\ValidationReportInterface $report) + { + if ($this->getIdentifier() === 'delete') { + $v = $this->createValidator()->platform('room-delete'); + if ( ! $v->evaluate($this->parameters['name'])) { + $report->addValidationError($this, 'Key', $v); + } + } + parent::validate($report); + } + + protected function onParametersSaved($parameters) + { + $actionName = $this->getIdentifier(); + if ($actionName === 'update') { + $actionName = 'modify'; + } + $this->getPlatform()->signalEvent(sprintf('room-%s &', $actionName)); + } + + public function prepareView(\Nethgui\View\ViewInterface $view) + { + parent::prepareView($view); + $templates = array( + 'create' => 'NethServer\Template\Doraemon\Rooms', + 'update' => 'NethServer\Template\Doraemon\Rooms', + 'delete' => 'Nethgui\Template\Table\Delete', + ); + $view->setTemplate($templates[$this->getIdentifier()]); + + + $owners = array(array('locals', $view->translate('locals_group_label'))); + $subjects = array(array('locals', $view->translate('locals_group_label'))); + + foreach ($this->getPlatform()->getDatabase('accounts')->getAll('group') as $keyName => $props) { + $entry = array($keyName, sprintf("%s (%s)", isset($props['Description']) ? $props['Description'] : $keyName, $keyName)); + $owners[] = $entry; + $subjects[] = $entry; + } + + $view['OwningGroupDatasource'] = $owners; + + foreach ($this->getPlatform()->getDatabase('accounts')->getAll('user') as $keyName => $props) { + $entry = array($keyName, sprintf("%s (%s)", trim($props['FirstName'] . ' ' . $props['LastName']), $keyName)); + $subjects[] = $entry; + } + + $view['AclSubjects'] = $subjects; + + } + +} diff --git a/root/usr/share/nethesis/NethServer/Template/Doraemon/Hosts.php b/root/usr/share/nethesis/NethServer/Template/Doraemon/Hosts.php new file mode 100644 index 0000000..eccb4a9 --- /dev/null +++ b/root/usr/share/nethesis/NethServer/Template/Doraemon/Hosts.php @@ -0,0 +1,22 @@ +getModule()->getIdentifier() == 'update') { + // disable updating of hostname + // the hostname is the key of the db item, if you change it, bad things happens + $keyFlags = $view::STATE_DISABLED; + $template = 'Update_Host_Header'; +} else { + $keyFlags = 0; + $template = 'Create_Host_Header'; +} + +echo $view->header('hostname')->setAttribute('template', $T($template)); + +echo $view->panel() + ->insert($view->textInput('hostname', $keyFlags)) + ->insert($view->textInput('MacAddress')) + ->insert($view->textInput('Role')) +; + +echo $view->buttonList($view::BUTTON_SUBMIT | $view::BUTTON_CANCEL | $view::BUTTON_HELP); diff --git a/root/usr/share/nethesis/NethServer/Template/Doraemon/Hosts/TogglePower.php b/root/usr/share/nethesis/NethServer/Template/Doraemon/Hosts/TogglePower.php new file mode 100644 index 0000000..4e16e40 --- /dev/null +++ b/root/usr/share/nethesis/NethServer/Template/Doraemon/Hosts/TogglePower.php @@ -0,0 +1,23 @@ +requireFlag($view::INSET_DIALOG); + +if ($view->getModule()->getIdentifier() == 'shutdown') { + $headerText = $T('Shutdown host `${0}`'); + $panelText = $T('Proceed with host `${0}` shutdown?'); +} elseif ($view->getModule()->getIdentifier() == 'reboot') { + $headerText = $T('Reboot host `${0}`'); + $panelText = $T('Proceed with host `${0}` reboot?'); +} else { + $headerText = $T('Wakeup host `${0}`'); + $panelText = $T('Proceed with host `${0}` wakeup?'); +} + +echo $view->panel() + ->insert($view->header('hostname')->setAttribute('template', $headerText)) + ->insert($view->textLabel('hostname')->setAttribute('template', $panelText)) +; + +echo $view->buttonList() + ->insert($view->button('Yes', $view::BUTTON_SUBMIT)) + ->insert($view->button('No', $view::BUTTON_CANCEL)->setAttribute('value', $view['Cancel'])) +; diff --git a/root/usr/share/nethesis/NethServer/Template/Doraemon/Hosts/TogglePowerAll.php b/root/usr/share/nethesis/NethServer/Template/Doraemon/Hosts/TogglePowerAll.php new file mode 100644 index 0000000..2d9968f --- /dev/null +++ b/root/usr/share/nethesis/NethServer/Template/Doraemon/Hosts/TogglePowerAll.php @@ -0,0 +1,21 @@ +requireFlag($view::INSET_DIALOG); + +if ($view->getModule()->getIdentifier() == 'shutdown-all') { + $headerText = $T('Shutdown all hosts'); + $panelText = $T('Proceed with all hosts shutdown?'); +} elseif ($view->getModule()->getIdentifier() == 'reboot-all') { + $headerText = $T('Reboot all hosts'); + $panelText = $T('Proceed with all hosts reboot?'); +} else { + $headerText = $T('Wakeup all hosts'); + $panelText = $T('Proceed with all hosts wakeup?'); +} + +echo $view->header()->setAttribute('template', $headerText); +// TODO: add a text label here +// echo $view->textLabel()->setAttribute('template', $panelText); +echo $view->buttonList() + ->insert($view->button('Yes', $view::BUTTON_SUBMIT)) + ->insert($view->button('No', $view::BUTTON_CANCEL)->setAttribute('value', $view['Cancel'])) +; diff --git a/root/usr/share/nethesis/NethServer/Template/Doraemon/Rooms.php b/root/usr/share/nethesis/NethServer/Template/Doraemon/Rooms.php new file mode 100644 index 0000000..723c7ef --- /dev/null +++ b/root/usr/share/nethesis/NethServer/Template/Doraemon/Rooms.php @@ -0,0 +1,40 @@ +requireFlag($view::INSET_FORM); + +if ($view->getModule()->getIdentifier() == 'update') { + // disable updating of hostname + // the hostname is the key of the db item, if you change it, bad things happens + $keyFlags = $view::STATE_DISABLED; + $template = 'Update_Room_Header'; +} else { + $keyFlags = 0; + $template = 'Create_Room_Header'; +} + +echo $view->header('doraemon')->setAttribute('template', $view->translate($template)); + +echo $view->panel() + ->insert($view->textInput('ibay', $keyFlags)) + ->insert($view->textInput('Description')) + ->insert($view->selector('OwningGroup', $view::SELECTOR_DROPDOWN)) + ->insert($view->checkBox('GroupAccess', 'rw')->setAttribute('uncheckedValue', 'r')) + ->insert($view->checkBox('OtherAccess', 'r')->setAttribute('uncheckedValue', '')) + ->insert($view->objectPicker() + ->setAttribute('objects', 'AclSubjects') + ->setAttribute('objectLabel', 1) + ->insert($view->checkBox('AclRead', FALSE, $view::STATE_CHECKED)) + ->insert($view->checkBox('AclWrite', FALSE)) +); + +echo $view->buttonList($view::BUTTON_SUBMIT | $view::BUTTON_CANCEL | $view::BUTTON_HELP); + +$actionId = $view->getUniqueId(); +$view->includeJavascript(" +jQuery(function($){ + $('#${actionId}').on('nethguishow', function () { + $(this).find('.Tabs').tabs('select', 0); + }); +}); +");