1
0
Fork 0
mirror of https://github.com/iiab/iiab.git synced 2025-02-12 11:12:06 +00:00

initial checkin -- May 27, 2017

This commit is contained in:
George Hunt 2017-05-27 11:09:50 -07:00
commit 845632d0ac
488 changed files with 41559 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
xs-config.spec
build
deprecated
.patches

339
COPYING Normal file
View file

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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 General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

16
LICENSE Normal file
View file

@ -0,0 +1,16 @@
COPYRIGHT and LICENSE
Many files in this repository have an explicit copyright notice and terms of license in the file.
Authors contributing to this repository are encouraged to provide a copyright notice and to license their work
under the terms of the GNU Library General Public License as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later version.
This license is contained in the file named COPYING. The simplest way to do this is to include
the following two lines at the top of the file:
# Copyright (C) 20xx <your name>
# Licensed under the terms of the GNU GPL v2 or later; see COPYING for details.
All files not containing an explicit copyright notice or terms of license in the file are Copyright © 2015, Unleash Kids,
and are licensed under the terms of the GPLv2 license in the file named COPYING in the root of the repository.

41
README.rst Normal file
View file

@ -0,0 +1,41 @@
======================================
School Server Community Edition (XSCE)
======================================
Welcome to the Git repository of the XSCE project. XSCE is a community-based
project developed and supported by volunteers from around the world. It
provides communication, networking, content, and maintenance to schools and
classrooms. In everyday usage the school server provides services which extend
capabilities of the connected laptops while being transparent to the
user. These services include:
* Classroom connectivity Similar to what you would find in an advanced home router.
* Internet gateway If available, an internet connection is made available to laptops.
* Content Tools to make instructional media available to their schools and classrooms.
* Maintenance Tools to keep laptop updated and running smoothly.
All of our server code resides in this repository. We are using ansible_ as the
underlying technology to install, deploy, configure and manage the various
server components.
Documentation has recently moved to the wiki of this repo
Please see the `XSCE wiki`_
Installation procedures are in the process of being reworked to include:
* Offline install on bare metal from a usb stick
* Offline install with an iso file
* Manual install of all or part of the server in combination with either of the above
Full installation instructions are in the wiki of this repo:
Please read the `installation`_ documentation.
See the `XSCE project`_ for more information about the project.
.. _XSCE wiki: https://github.com/XSCE/xsce/wiki
.. _installation: https://github.com/XSCE/xsce/wiki/XSCE-Installation
.. _ansible: http://www.ansibleworks.com/
.. _ansible documentation: http://www.ansibleworks.com/docs/
.. _XSCE project: http://schoolserver.org/

53
ReleaseNotes6.0.md Normal file
View file

@ -0,0 +1,53 @@
# Release Notes for XSCE 6.0
## What is it?
XSCE is the digital backbone for your education revolution. Put simply, School Server is an open-education project inspired by
One Laptop Per Child to provide breakthrough digital learning tools to the worlds poorest children.
### A Rose by any other Name
Internally and in the source code we refer to this software as XSCE, the Community Edition of the XS server originally from
OLPC. Students and teachers often know it as Internet in a Box, a collection of educational materials and applications found on the internet,
but available on the server even without an internet connection. We also refer to it as a School Server since it is aimed at
schools and other places where people learn and performs the function of a server.
## What's new in XSCE Release 6.0?
This release extends the tools available to educators in three ways:
* It makes available free and open source content that is available in many languages
* It provides new avenues for students to create, and share their work
* It contains number of advances to permit easy configuration, tailoring of content, and monitoring of student work.
### Newly Available Open Source Educational Content
* KA Lite brings the online features of **Khan Academy** to the **offline** schoolserver based classroom. There are videos, exercises, tests, and student tracking in a number of languages, all selectable via a Graphical User Interface. https://learningequality.org/ka-lite/
* Searchable **offline** access to **Wikipedia** and other content such as Wiktionaries and TED Talks in a variety of subjects and many languages, provided by the **Kiwix server** technology. http://www.kiwix.org/wiki/Main_Page
* RACHEL (a currated selection of offline materials) http://worldpossible.org/rachel/
* Bring your **own content** by inserting a **USB thumbdrive** with content into the server having it immediately viewable by students.
### Encourage Students to Write, Foster the Creative Process
* Elgg provides the tools for generating social networks http://learn.elgg.org/en/2.0/intro/features.html
* ownCloud provides client based tools for storing and retrieving materials stored on the XSCE local cloud.
* DokuWiki (EXPERIMENTAL) provides a means for students to publish their work. https://www.dokuwiki.org/features
* Wordpress (EXPERIMENTAL -- enable in "local_vars") gives students experience editing/sharing using a tool that is becoming an industry standard.
### New Tools for Administering the XSCE schoolserver
* A new Graphical User Interface (Admin Console) to enable services, select and download content, get information
* AWstats is a flexible tool for summarizing the web traffic on and through the server in graphical and detailed ways. http://www.awstats.org/
## How do I get it?
There are three main methods of installing this software:
* Use ansible and the git repository - this is the fall back when you need to customize or you have a platform for which we have not created an image.
* Use an image you download - for some commonly used platforms we create image files that you can download, put on a usb stick or sd card and boot in your new hardware.
* Create and use your own image - this will appeal to you if you want an image to use in multiple machines and are able to use the tools we provide to create it.
In each case you need hardware that has been assembled, but with nothing installed on it.
Detailed instructions on each of these methods is at https://github.com/XSCE/xsce/wiki/XSCE-Installation.

36
ReleaseNotes6.1.md Normal file
View file

@ -0,0 +1,36 @@
# Release Notes for Release 6.1
**What's New?**
* Calibre -- A tool for managing a library of eBooks, modifyiing their file formats, adding search terms, and making them availabe online.
* Wordpress -- A content management system which gives students experience with editing wiki pages, blogs, menuing systems, and which is widely used.
* Dokuwiki -- An alternate wiki system, similar to wordpress, but less popular, which makes transferring wiki materials easy from one school server to another.
* Sugarizer -- Makes some of the sugar activities available to browser clients on laptops, and smart phones/tablets.
* CUPS -- Common Unix Printing System provides the ability to connect to and share network or USB connected printers.
**What's Upgraded?**
* Moodle is now upgraded to version 3.1, the most recent (long term support) version that will be supported until May 2019.
* Elgg -- A social networking application is upgraded to 2.1.
* Owncloud -- Permits sharing of all kinds of content between clients of a local server that is not internet connected (version 9).
**Do all these new Services Slow my Server down?**
A service that is installed on your hard disk, but not enabled in the administrative console, will have no impact on computer speed (will not use cpu cycles, or occupy scarce memory resources). The XSCE default is to install everything, and only enable the few things which are essential for a server to operate.
If you want to enable a service, you must browse to http://schoolserver.lan/admin, and click on configure, services enabled, and the appropriate checkbox. In addition, many services require additional content to be downloaded, which can also be accomplished by selecting the "Install Content" header button.
**How Do I Install 6.1?**
The install instructions have not changed much since release-6.0. Please refer to https://github.com/XSCE/xsce/wiki/XSCE-Installation for the overall process -- Noting the following:
* On FC22, add "yum" to the installs prior to running the ansible playbook i.e.
```
yum install -y git yum ansible1.9
cd /opt
mkdir /opt/schoolserver
cd schoolserver
git clone https://github.com/XSCE/xsce --branch release-6.1 --depth 1
cd xsce
./install-console
```

193
ansible.cfg Normal file
View file

@ -0,0 +1,193 @@
# config file for ansible -- http://ansible.com/
# ==============================================
# nearly all parameters can be overridden in ansible-playbook
# or with command line flags. ansible will read ANSIBLE_CONFIG,
# ansible.cfg in the current working directory, .ansible.cfg in
# the home directory or /etc/ansible/ansible.cfg, whichever it
# finds first
[defaults]
# some basic default values...
hostfile = /etc/ansible/hosts
library = /usr/share/ansible
remote_tmp = $HOME/.ansible/tmp
pattern = *
forks = 5
poll_interval = 15
sudo_user = root
#ask_sudo_pass = True
#ask_pass = True
transport = smart
remote_port = 22
module_lang = C
# plays will gather facts by default, which contain information about
# the remote system.
#
# smart - gather by default, but don't regather if already gathered
# implicit - gather by default, turn off with gather_facts: False
# explicit - do not gather by default, must say gather_facts: True
gathering = implicit
# additional paths to search for roles in, colon separated
#roles_path = /etc/ansible/roles
# uncomment this to disable SSH key host checking
#host_key_checking = False
# change this for alternative sudo implementations
sudo_exe = sudo
# what flags to pass to sudo
#sudo_flags = -H
# SSH timeout
timeout = 10
# default user to use for playbooks if user is not specified
# (/usr/bin/ansible will use current user as default)
#remote_user = root
# logging is off by default unless this path is defined
# if so defined, consider logrotate
#log_path = /var/log/ansible.log
# default module name for /usr/bin/ansible
#module_name = command
# use this shell for commands executed under sudo
# you may need to change this to bin/bash in rare instances
# if sudo is constrained
#executable = /bin/sh
# if inventory variables overlap, does the higher precedence one win
# or are hash values merged together? The default is 'replace' but
# this can also be set to 'merge'.
#hash_behaviour = replace
# list any Jinja2 extensions to enable here:
#jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n
# if set, always use this private key file for authentication, same as
# if passing --private-key to ansible or ansible-playbook
#private_key_file = /path/to/file
# format of string {{ ansible_managed }} available within Jinja2
# templates indicates to users editing templates files will be replaced.
# replacing {file}, {host} and {uid} and strftime codes with proper values.
ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}
# by default, ansible-playbook will display "Skipping [host]" if it determines a task
# should not be run on a host. Set this to "False" if you don't want to see these "Skipping"
# messages. NOTE: the task header will still be shown regardless of whether or not the
# task is skipped.
#display_skipped_hosts = True
# by default (as of 1.3), Ansible will raise errors when attempting to dereference
# Jinja2 variables that are not set in templates or action lines. Uncomment this line
# to revert the behavior to pre-1.3.
#error_on_undefined_vars = False
# by default (as of 1.6), Ansible may display warnings based on the configuration of the
# system running ansible itself. This may include warnings about 3rd party packages or
# other conditions that should be resolved if possible.
# to disable these warnings, set the following value to False:
system_warnings = False
# by default (as of 1.4), Ansible may display deprecation warnings for language
# features that should no longer be used and will be removed in future versions.
# to disable these warnings, set the following value to False:
deprecation_warnings = False
# set plugin path directories here, separate with colons
action_plugins = /usr/share/ansible_plugins/action_plugins
callback_plugins = /usr/share/ansible_plugins/callback_plugins
connection_plugins = /usr/share/ansible_plugins/connection_plugins
lookup_plugins = /usr/share/ansible_plugins/lookup_plugins
vars_plugins = /usr/share/ansible_plugins/vars_plugins
filter_plugins = /usr/share/ansible_plugins/filter_plugins
# don't like cows? that's unfortunate.
# set to 1 if you don't want cowsay support or export ANSIBLE_NOCOWS=1
#nocows = 1
# don't like colors either?
# set to 1 if you don't want colors, or export ANSIBLE_NOCOLOR=1
#nocolor = 1
# the CA certificate path used for validating SSL certs. This path
# should exist on the controlling node, not the target nodes
# common locations:
# RHEL/CentOS: /etc/pki/tls/certs/ca-bundle.crt
# Fedora : /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem
# Ubuntu : /usr/share/ca-certificates/cacert.org/cacert.org.crt
#ca_file_path =
# the http user-agent string to use when fetching urls. Some web server
# operators block the default urllib user agent as it is frequently used
# by malicious attacks/scripts, so we set it to something unique to
# avoid issues.
#http_user_agent = ansible-agent
[paramiko_connection]
# uncomment this line to cause the paramiko connection plugin to not record new host
# keys encountered. Increases performance on new host additions. Setting works independently of the
# host key checking setting above.
#record_host_keys=False
# by default, Ansible requests a pseudo-terminal for commands executed under sudo. Uncomment this
# line to disable this behaviour.
#pty=False
[ssh_connection]
# ssh arguments to use
# Leaving off ControlPersist will result in poor performance, so use
# paramiko on older platforms rather than removing it
#ssh_args = -o ControlMaster=auto -o ControlPersist=60s
# The path to use for the ControlPath sockets. This defaults to
# "%(directory)s/ansible-ssh-%%h-%%p-%%r", however on some systems with
# very long hostnames or very long path names (caused by long user names or
# deeply nested home directories) this can exceed the character limit on
# file socket names (108 characters for most platforms). In that case, you
# may wish to shorten the string below.
#
# Example:
# control_path = %(directory)s/%%h-%%r
#control_path = %(directory)s/ansible-ssh-%%h-%%p-%%r
# Enabling pipelining reduces the number of SSH operations required to
# execute a module on the remote server. This can result in a significant
# performance improvement when enabled, however when using "sudo:" you must
# first disable 'requiretty' in /etc/sudoers
#
# By default, this option is disabled to preserve compatibility with
# sudoers configurations that have requiretty (the default on many distros).
#
#pipelining = False
# if True, make ansible use scp if the connection type is ssh
# (default is sftp)
#scp_if_ssh = True
[accelerate]
accelerate_port = 5099
accelerate_timeout = 30
accelerate_connect_timeout = 5.0
# The daemon timeout is measured in minutes. This time is measured
# from the last activity to the accelerate daemon.
accelerate_daemon_timeout = 30
# If set to yes, accelerate_multi_key will allow multiple
# private keys to be uploaded to it, though each user must
# have access to the system via SSH to add a new key. The default
# is "no".
#accelerate_multi_key = yes
command_warnings = False

3
ansible_hosts Normal file
View file

@ -0,0 +1,3 @@
[localhost]
127.0.0.1

51
install-console Executable file
View file

@ -0,0 +1,51 @@
#!/bin/bash
# copy var files to /etc/xsce for subsequent use
#./install-init
# if not the first run, repo location is here
if [ -f /etc/xsce/xsce.env ]
then
. /etc/xsce/xsce.env
cd $XSCE_DIR
else
XSCE_DIR=/opt/schoolserver/xsce
mkdir -p /etc/xsce
touch /etc/xsce/config_vars.yml
fi
# don't track vars/local_vars.yml
git update-index --assume-unchanged vars/local_vars.yml
PLAYBOOK="xsce-base.yml"
INVENTORY="ansible_hosts"
SELINUX_BEFORE=""
SELINUX_AFTER=""
if [ ! -f $PLAYBOOK ]
then
echo "XSCE Playbook not found."
echo "Please run this command from the top level of the git repo."
echo "Exiting."
exit 1
fi
if [ -f /etc/selinux/config ]
then
SELINUX_BEFORE=`cat /etc/selinux/config | gawk -F= '/^SELINUX=/{ print $2 }'`
fi
export ANSIBLE_LOG_PATH="$XSCE_DIR/xsce-install.log"
ansible-playbook -i $INVENTORY $PLAYBOOK --connection=local
if [ -f /etc/selinux/config ]
then
SELINUX_AFTER=`cat /etc/selinux/config | gawk -F= '/^SELINUX=/{ print $2 }'`
fi
if [ "$SELINUX_BEFORE" != "$SELINUX_AFTER" ]; then
echo "Rebooting ..."
reboot
fi

6
roles/1-prep/README.rst Normal file
View file

@ -0,0 +1,6 @@
===========
Prep README
===========
This role is a sort on init or startup. It includes preliminaries like hostname and is where things
that are specific to a particular platform, such as the XO, are done before the bulk of the install.

View file

@ -0,0 +1,57 @@
# use these as a tag a release at a point in time
xsce_base_ver: 0
gui_version: 2
NUC6_firmware_needed: False
# These entries should never be changed in this file.
# These are defaults for boolean routines,
installing: False
exFAT_enabled: False
no_NM_reload: False
has_WAN: False
wireless_lan_present: False
udev_needs_patch: False
strict_networking: False
xsce_demo_mode: False
gw_active: False
gui_static_wan: False
has_internet_connection: False
is_F18: False
is_F20: False
is_F21: False
is_F22: False
is_F23: False
is_F24: False
# Set default for discovered hardware
driver_name: nl80211
rpi_model: none
is_rpi: False
xo_model: none
rtc_id: none
# Set defaults for discovery process as strings
wifi1: "not found-1"
wifi2: "not found-2"
discovered_wan_iface: "none"
discovered_lan_iface: "none"
discovered_wireless_iface: "none"
xsce_wireless_lan_iface: "none"
xsce_lan_iface: "none"
xsce_wan_iface: "none"
device_gw: "none"
has_ifcfg_gw: "none"
has_wifi_gw: "none"
ap_device: "none"
device_gw2: ""
# WiFi
ssid: XSCE
hostapd_wait: 10
gui_port: 80
# must keep roles/xsce-admin/defaults/main.yml sync'd
admin_console_path: "{{ xsce_base }}/admin_console"
cmdsrv_path: "{{ xsce_base }}/xsce_cmdsrv"
xsce_cmdsrv_dbname : "xsce_cmdsrv.0.2.db"
wifi_id: none

View file

@ -0,0 +1,162 @@
# get local vars from scripts in /etc/ansible/facts.d
# on first run, this will generate UUID
- name: re-read facts
setup: filter=ansible_local
# set top level variables from local facts for convenience
- set_fact:
xo_model: '{{ ansible_local["local_facts"]["xo_model"] }}'
phplib_dir: '{{ ansible_local["local_facts"]["phplib_dir"] }}'
xsce_base_ver: '{{ ansible_local["local_facts"]["xsce_base_ver"] }}'
xsce_preload: '{{ ansible_local["local_facts"]["xsce_preload"] }}'
- name: Defaulting xsce_base_ver
set_fact:
xsce_base_ver: 0
when: xsce_base_ver == ""
- name: Defaulting xsce_prepped
set_fact:
xsce_prepped: False
tags:
- download
- download2
- name: Set exFAT enabled for XOs
set_fact:
exFAT_enabled: True
when: xo_model != "none"
- name: add version section
ini_file: dest='{{ xsce_config_file }}'
section=runtime
option='{{ item.option }}'
value='{{ item.value }}'
with_items:
- option: 'runtime_branch'
value: '{{ ansible_local["local_facts"]["xsce_branch"] }}'
- option: 'runtime_commit'
value: '{{ ansible_local["local_facts"]["xsce_commit"] }}'
- option: 'runtime_date'
value: '{{ ansible_date_time["iso8601"] }}'
- option: 'runtime_php'
value: '{{ phplib_dir }}'
- option: 'runtime_preload'
value: '{{ xsce_preload }}'
- option: 'runtime_base_ver'
value: '{{ xsce_base_ver }}'
- option: 'kernel'
value: '{{ ansible_kernel }}'
- option: 'memory_mb'
value: '{{ ansible_memtotal_mb }}'
- option: 'swap_mb'
value: '{{ ansible_swaptotal_mb }}'
- option: 'product_id'
value: '{{ ansible_product_uuid }}'
# Put all computed vars here so derive properly from any prior var file
- name: Set port 80 for Admin Console
set_fact:
gui_port: 80
when: not adm_cons_force_ssl
- name: Set port 443 for Admin Console
set_fact:
gui_port: 443
when: adm_cons_force_ssl
- name: Turn on mysql if elgg or rachel enabled
set_fact:
mysql_install: True
mysql_enabled: True
# we decided to enable mysql unconditionally
# when: elgg_enabled or rachel_enabled or owncloud_enabled or phpmyadmin_enabled or wordpress_enabled or iiab_menu_install
- name: Turn on mongodb if sugarizer enabled
set_fact:
mongodb_install: True
mongodb_enabled: True
when: sugarizer_enabled
# There might be other db's
- name: Turn on postgresql if moodle or pathagar enabled
set_fact:
postgresql_install: True
postgresql_enabled: True
when: moodle_enabled or pathagar_enabled
- name: Turn on docker if schooltool is to be installed
set_fact:
docker_install: True
docker_enabled: True
when: schooltool_enabled or schooltool_install
# for various reasons the mysql service can not be enabled on fedora 20,
# but 'mariadb', which is its real name can
# on fedora 18 we need to use 'mysqld'
- name: Set mysqld service name to mariadb by default
set_fact:
mysql_service: mariadb
- name: Set mysqld service name to mysqld for fedora 18
set_fact:
mysql_service: mysqld
no_NM_reload: True
is_F18: True
when: ansible_distribution_release == "based on Fedora 18" or ansible_distribution_version == "18"
- name: Set mysql service name to mysql for debian
set_fact:
mysql_service: mysql
when: ansible_distribution == "Debian"
- name: Fedora 20
set_fact:
is_F20: True
when: ansible_distribution == "Fedora" and ansible_distribution_version == "20"
- name: Fedora 21
set_fact:
is_F21: True
when: ansible_distribution == "Fedora" and ansible_distribution_version == "21"
- name: Fedora 22
set_fact:
is_F22: True
when: ansible_distribution == "Fedora" and ansible_distribution_version == "22"
- name: Fedora 23
set_fact:
is_F23: True
when: ansible_distribution == "Fedora" and ansible_distribution_version == "23"
- name: Fedora 24
set_fact:
is_F24: True
when: ansible_distribution == "Fedora" and ansible_distribution_version == "24"
- name: CentOS
set_fact:
is_CentOS: True
when: ansible_distribution == "CentOS"
- name: Set kiwix source file name i686
set_fact:
kiwix_src_file: "kiwix-linux-i686.tar.bz2"
kiwix_src_bin_only: False
when: ansible_machine == "i686"
- name: Set kiwix source file name x86_64
set_fact:
kiwix_src_file: "kiwix-0.9-linux-x86_64.tar.bz2"
kiwix_src_bin_only: False
when: ansible_machine == "x86_64"
- name: Set kiwix source file name armv7l
set_fact:
kiwix_src_file: "kiwix-server-0.9-linux-armv5tejl.tar.bz2"
kiwix_src_bin_only: True
when: ansible_machine == "armv7l"

View file

@ -0,0 +1,305 @@
- name: Checking xs_domain_name
shell: "cat /etc/sysconfig/xs_domain_name"
register: prior_domain
ignore_errors: True
# above always registers
- name: Checking for prior domain name
set_fact:
xsce_domain: "{{ prior_domain.stdout }}"
when: prior_domain.stdout != "lan" and prior_domain.stdout != ""
- name: xs_wan_device
shell: "cat /etc/sysconfig/xs_wan_device"
register: prior_gw
ignore_errors: True
- name: Checking for old device gateway interface for device test
set_fact:
device_gw: "{{ prior_gw.stdout }}"
device_gw2: "{{ prior_gw.stdout }}"
when: prior_gw is defined and prior_gw.stdout != ""
- name: Checking for ifcfg-WAN file
stat: path=/etc/sysconfig/network-scripts/ifcfg-WAN
register: has_ifcfg_WAN
- name: Setting ifcfg-WAN True
set_fact:
has_WAN: True
when: has_ifcfg_WAN.stat.exists
# DETECT -- gateway and wireless
- name: Get a list of slaves from previous config
shell: "egrep -rn BRIDGE=br0 /etc/sysconfig/network-scripts/ifcfg-* | gawk -F'[-:]' '{print $3}'"
register: ifcfg_slaves
ignore_errors: True
changed_when: False
# Discover do we have a gateway? -- if ansible detects gateway, becomes WAN candidate
- name: Finding gateway
set_fact:
discovered_wan_iface: "{{ ansible_default_ipv4.alias }}"
when: 'ansible_default_ipv4.gateway is defined'
- name: Verify gateway present
shell: ping -c2 "{{ ansible_default_ipv4.gateway }}" &> /dev/null ; echo $?
register: gw_active_test
when: discovered_wan_iface != "none"
- name: Recording gateway response
set_fact:
gw_active: True
when: discovered_wan_iface != "none" and gw_active_test.stdout == "0"
- name: Test for internet access
get_url: url="{{ xsce_download_url }}/heart-beat.txt" dest=/tmp/heart-beat.txt
ignore_errors: True
# async: 10
# poll: 2
register: internet_access_test
- name: Set has_internet_connection true if wget succeeded
set_fact:
has_internet_connection: True
when: not internet_access_test|failed
- name: Cleanup internet test file
file: path=/tmp/heart-beat.txt
state=absent
- name: Turn off downloads if no internet connection
set_fact:
no_network: True
when: not has_internet_connection
- name: Testing for xsce_preload
set_fact:
use_cache: True
no_network: True
when: xsce_preload == "True"
- name: Setting wan if detected
set_fact:
xsce_wan_iface: "{{ discovered_wan_iface }}"
device_gw: "{{ discovered_wan_iface }}"
when: discovered_wan_iface != "none"
# returns list of paths
- name: Find gateway config based on device
shell: "egrep -rn {{ device_gw }} /etc/sysconfig/network-scripts/ifcfg* | gawk -F ':' '{print $1}'"
register: ifcfg_gw_device
ignore_errors: True
changed_when: False
when: device_gw != "none"
# last match wins
- name: Setting has ifcfg gw based on device if found
set_fact:
has_ifcfg_gw: "{{ item|trim }}"
ignore_errors: True
when: ifcfg_gw_device.stdout_lines is defined and item|trim != "" and item|trim != "/etc/sysconfig/network-scripts/ifcfg-LAN"
with_items:
- "{{ ifcfg_gw_device.stdout_lines }}"
# returns path
- name: Find active gateway config based on macaddress
shell: "egrep -irn {{ ansible_default_ipv4.macaddress }} /etc/sysconfig/network-scripts/ifcfg* | gawk -F ':' '{print $1}' | head -n 1"
register: ifcfg_gw_mac
ignore_errors: True
changed_when: False
when: 'ansible_default_ipv4.gateway is defined'
- name: Set has ifcfg gw based on on macaddress if found
set_fact:
has_ifcfg_gw: "{{ ifcfg_gw_mac.stdout|trim }}"
when: ifcfg_gw_mac is defined and ifcfg_gw_mac.stdout != ""
# could use something else
- name: Find wifi gateway config if present
shell: egrep -rn ESSID /etc/sysconfig/network-scripts/ifcfg* | gawk -F ':' '{print $1}' | gawk -F '/' '{print $5}'
register: ifcfg_WAN_wifi
ignore_errors: True
#returns file name
- name: Setting has_wifi_gw based on ESSID if found
set_fact:
has_wifi_gw: "{{ item|trim }}"
when: ifcfg_WAN_wifi.changed and item|trim != ""
with_items:
- "{{ ifcfg_WAN_wifi.stdout_lines }}"
- name: Finding device for wifi AP gateway
shell: egrep -rn DEVICE /etc/sysconfig/network-scripts/{{ has_wifi_gw }} | gawk -F '=' '{print $2}'
register: AP_device
when: has_wifi_gw != "none" and has_ifcfg_gw != "none"
- name: Setting wifi device
set_fact:
ap_device: "{{ AP_device.stdout }}"
when: AP_device.stdout is defined and AP_device.stdout != ""
# WIRELESS -- if any wireless is detected as gateway, it becomes WAN
- name: Look for any wireless interfaces
shell: "cat /proc/net/wireless | grep -v -e Inter -e face | gawk -F: '{print $1}' "
register: wireless_list1
ignore_errors: True
changed_when: False
- name: Set the discovered wireless, if found
set_fact:
wifi1: "{{ item|trim }}"
discovered_wireless_iface: "{{ item|trim }}"
when: item|trim != "" and item|trim != discovered_wan_iface
with_items:
- "{{ wireless_list1.stdout_lines }}"
# WIRELESS -- Sigh... Not all drivers update /proc/net/wireless correctly
- name: Look for any wireless interfaces take 2
shell: "ls -la /sys/class/net/*/phy80211 | awk -F / '{print $5}'"
register: wireless_list2
ignore_errors: True
changed_when: False
# Last device is used
- name: Set the discovered wireless, if found take 2
set_fact:
wifi2: "{{ item|trim }}"
discovered_wireless_iface: "{{ item|trim }}"
when: wireless_list2.stdout != "" and item|trim != discovered_wan_iface
with_items:
- "{{ wireless_list2.stdout_lines }}"
- name: Count Wifi ifaces
shell: "ls -la /sys/class/net/*/phy80211 | awk -F / '{print $5}' | wc -l"
register: count_wifi_interfaces
- name: Remember number of Wifi devices
set_fact:
num_wifi_interfaces: "{{ count_wifi_interfaces.stdout|int }}"
# XO hack here ap_device would not be active therefore not set with
# wired as gw use ap_device to exclude eth0 from network calulations
- name: XO override 2 wifi on LAN
set_fact:
ap_device: "eth0"
when: xsce_wan_iface != "eth0" and discovered_wireless_iface != "none" and xo_model == "XO-1.5"
# takes adapter name
- name: Blacklisted wifi adapter
set_fact:
ap_device: "{{ blacklist_wifi }}"
when: blacklist_wifi is defined and discovered_wireless_iface != xsce_wan_iface and num_wifi_interfaces >= "2"
# LAN - pick non WAN's
- name: Create list of LAN (non wan) ifaces
shell: ls /sys/class/net | grep -v -e wwlan -e ppp -e lo -e br0 -e tun -e {{ device_gw }} -e {{ ap_device }}
register: lan_list_result
ignore_errors: True
changed_when: false
# Select an adapter that is not WAN and not wireless
# if there is more than one the last one wins
- name: Set xsce discovered lan fact
set_fact:
discovered_lan_iface: "{{ item|trim }}"
when: item|trim != discovered_wireless_iface and item|trim != discovered_wan_iface
with_items:
- "{{ lan_list_result.stdout_lines }}"
- name: Count LAN ifaces
shell: ls /sys/class/net | grep -v -e wwlan -e ppp -e lo -e br0 -e tun -e {{ device_gw }} -e {{ ap_device }} | wc -l
register: num_lan_interfaces_result
ignore_errors: True
changed_when: false
# facts are apparently all stored as text, so do text comparisons from here on
- name: Calulate number of LAN interfaces including WiFi
set_fact:
num_lan_interfaces: "{{ num_lan_interfaces_result.stdout|int }}"
# If 2 interfaces found in gateway mode, with one wifi, declare other to be wan
#- name: In gateway mode with one wifi adapter, the other is WAN
# set_fact:
# xsce_wan_iface: "{{ discovered_lan_iface }}"
# xsce_lan_iface: "{{ discovered_wireless_iface }}"
# num_lan_interfaces: "1"
# when: xsce_lan_enabled and xsce_wan_enabled and num_lan_interfaces == "2" and discovered_wireless_iface != "none" and xsce_wan_iface == "none"
- name: Set the variable for wireless_iface if present
set_fact:
xsce_wireless_lan_iface: "{{ discovered_wireless_iface }}"
when: discovered_wireless_iface != "none" and discovered_wireless_iface != xsce_wan_iface
#unused
- name: Get a list of ifcfg files to delete
shell: "ls -1 /etc/sysconfig/network-scripts/ifcfg-* | grep -v -e ifcfg-lo -e ifcfg-WAN -e {{ has_wifi_gw }}"
register: ifcfg_files
changed_when: False
ignore_errors: True
when: num_lan_interfaces >= "1" or xsce_wireless_lan_iface != "none"
#
# use value only if present
- name: Setting detected lan
set_fact:
xsce_lan_iface: "{{ discovered_lan_iface }}"
when: 'discovered_lan_iface != "none" and num_lan_interfaces == "1"'
- name: for debian, always use bridging
set_fact:
xsce_lan_iface: br0
when: 'discovered_lan_iface != "none" and num_lan_interfaces >= "1" and is_debian'
- name: 2 or more devices on the LAN - use bridging
set_fact:
xsce_lan_iface: br0
when: 'discovered_lan_iface != "none" and num_lan_interfaces >= "2" and not is_debian'
- name: WiFi is on the LAN - use bridging
set_fact:
xsce_lan_iface: br0
when: xsce_wireless_lan_iface != "none"
# OK try old gw this is a best guess based on what's in
# /etc/sysconfig/xs_wan_device's last state intended to
# provide a seed value to display in the GUI when no
# gateway is present but we had one.
- name: Has old gateway and no discovered gateway setting WAN
set_fact:
gui_wan_iface: "{{ device_gw }}"
when: user_wan_iface == "auto" and device_gw != "none" and discovered_wan_iface == "none"
- name: Add location section to config file
ini_file: dest='{{ xsce_config_file }}'
section=network
option='{{ item.option }}'
value='{{ item.value }}'
with_items:
- option: 'gateway_active'
value: '{{ gw_active }}'
- option: 'internet_accessible'
value: '{{ has_internet_connection }}'
- option: 'gateway_ifcfg'
value: '{{ has_ifcfg_gw }}'
- option: 'detected_gateway'
value: '{{ discovered_wan_iface }}'
- option: 'prior_gateway'
value: '{{ device_gw2 }}'
- option: 'wireless_list_1'
value: '{{ wifi1 }}'
- option: 'wireless_list_2'
value: '{{ wifi2 }}'
- option: 'num_wifi_interfaces'
value: '{{ num_wifi_interfaces }}'
- option: 'discovered_wireless_iface'
value: '{{ discovered_wireless_iface }}'
- option: 'xsce_wireless_lan_iface'
value: '{{ xsce_wireless_lan_iface }}'
- option: 'num_lan_interfaces'
value: '{{ num_lan_interfaces }}'
- option: 'detected_lan'
value: '{{ discovered_lan_iface }}'
- option: 'static_wan'
value: '{{ gui_static_wan }}'

102
roles/1-prep/tasks/main.yml Normal file
View file

@ -0,0 +1,102 @@
- name: Set flag for fedora 18
set_fact:
is_F18: True
when: ansible_distribution_release == "based on Fedora 18" or ansible_distribution_version == "18"
- name: get the uuidgen program
package: name=uuid-runtime
state=present
when: is_debian
# for rpi, without rtc, we need time as soon as possible
- name: Install chrony package
package: name={{ item }}
state=present
with_items:
- chrony
tags:
- download
#TODO: Use regexp filter instead of hard-code ip
- name: Update chrony config file
template: backup=no
dest=/etc/chrony.conf
src=chrony.conf.j2
- name: Disable selinux on next boot
selinux: state=disabled
register: selinux_disabled
when: ansible_selinux is defined and ansible_version >= '2' and ansible_selinux or ansible_selinux['status'] is defined and ansible_version < '2' and ansible_selinux['status'] != 'disabled'
- name: Disable selinux for this session (if needed)
command: setenforce Permissive
when: selinux_disabled is defined and selinux_disabled.changed
## DISCOVER PLATFORMS ######
- name: Discover if this is a rpi -- if so it has a bcm2709 processor
set_fact:
rpi_model: "rpi"
is_rpi: "True"
when: ansible_cmdline["bcm2709.serial"] is defined
ignore_errors: true
- include: prep.yml
# we need to inialize the ini file
- include: xsce_ini.yml
- include: computed_vars.yml
- include: detected_network.yml
when: not installing
# Patch Fedora 21+ so usbmount works
# Set flag to True that was initialized to False in role defaults
# We can't undo this
- name: Does udev need patching
set_fact:
udev_needs_patch: True
when: ansible_distribution == "Fedora" and ansible_distribution_version >= "21"
# Same patch for CentOS 7.2
- name: Does udev need patching for Centos
set_fact:
udev_needs_patch: True
when: ansible_distribution == "CentOS" and ansible_distribution_version >= "7.2.1511"
- name: Copy udevd service to /etc/systemd/system to modify
copy: src=/usr/lib/systemd/system/systemd-udevd.service
dest=/etc/systemd/system/systemd-udevd.service
owner=root
group=root
mode=0644
when: udev_needs_patch
- name: Change MountFlags from slave to shared
lineinfile: backup=no
dest=/etc/systemd/system/systemd-udevd.service
regexp='^MountFlags'
line='MountFlags=shared'
state=present
when: udev_needs_patch
- name: Restart systemd-udevd.service
service: name=systemd-udevd
state=restarted
when: udev_needs_patch and not installing
# Put conditional actions for hardware platforms here
- include: raspberry_pi_2.yml
when: rpi_model != "none"
- name: Check if the identifier for intel's NUC6 builtin wifi is present
shell: "lsusb | grep 8087:0a2b | wc |gawk '{print $1}'"
register: usb_NUC6
ignore_errors: true
- name: download the firmware for built in wifi on NUC6
get_url: dest=/lib/firmware
url={{ xsce_download_url }}/iwlwifi-8000C-13.ucode
when: usb_NUC6.stdout|int > 0

View file

@ -0,0 +1,74 @@
- name: Since f22, dnf has replaced yum, but ansible works with yum
command: dnf install -y yum
when: ansible_distribution == "Fedora" and ansible_distribution_version|int >= 22
- name: Install xsce-extra repos
template: backup=yes
dest=/etc/yum.repos.d/xsce-extra.repo
src=xsce-extra.repo
owner=root
mode=0666
when: ansible_distribution != "Debian"
- name: Install xsce-testing repos
template: backup=yes
dest=/etc/yum.repos.d/xsce-testing.repo
src=xsce-testing.repo
owner=root
mode=0666
when: ansible_distribution != "Debian"
- name: Install rpmfusion-free-updates repo -- for exfat
template: dest=/etc/yum.repos.d/rpmfusion-free-updates.repo
src=rpmfusion-free-updates.repo
owner=root
mode=0666
when: ansible_distribution == "Fedora"
- name: Create /etc/xsce
file: path=/etc/xsce
owner=root
group=root
mode=0755
state=directory
# this script can be sourced to get xsce location
- name: Create xsce.env file
template: src=xsce.env.j2
dest=/etc/xsce/xsce.env
owner=root
group=root
mode=0644
- name: create ansible.d facts directory
file: path=/etc/ansible/facts.d
owner=root
group=root
mode=0750
state=directory
- name: local facts script
template: src=local_facts.fact.j2
dest=/etc/ansible/facts.d/local_facts.fact
owner=root
group=root
mode=0700
- name: re-read facts
setup: filter=ansible_local
- name: Set XO model and preload
set_fact:
phplib_dir: '{{ ansible_local["local_facts"]["phplib_dir"] }}'
xo_model: '{{ ansible_local["local_facts"]["xo_model"] }}'
xsce_preload: '{{ ansible_local["local_facts"]["xsce_preload"] }}'
- name: Install script to fully initialize network config, and/or collect data
# calling xs-network-reset w/ snapshot name, stores info, but aborts reset
# intended as a convenience function for us
template: src=xs-network-reset
dest=/usr/bin
owner=root
group=root
mode=0755

View file

@ -0,0 +1,62 @@
# Setup specific to the Raspberry Pi
#
- name: Add a udev rule to transfer hwclock to system clock at dev creation
template: src=92-rtc-i2c.rules
dest=/etc/udev/rules.d/92-rtc-i2c.rules
owner=root
group=root
mode=0644
when: rtc_id is defined and rtc_id != "none"
#
# RTC requires a change to the device tree (and reboot)
- name: Check for needing to enable i2c rtc device in config.txt
lineinfile: dest=/boot/config.txt
line="dtoverlay=i2c-rtc,{{ rtc_id }}=on"
state=present
register: rpiconfig
when: rtc_id != "none"
- name: Add a udev rule to transfer hwclock to system clock at dev creation
template: src=92-rtc-i2c.rules
dest=/etc/udev/rules.d/92-rtc-i2c.rules
owner=root
group=root
mode=0644
when: rtc_id != "none"
- name: pre-Install packages
package: name={{ item }}
state=latest
with_items:
- ntp
- name: increase the swap file size (kalite pip download fails)
lineinfile: regexp="^CONF_SWAPSIZE"
line=CONF_SWAPSIZE=500
dest=/etc/dphys-swapfile
when: is_debian
- name: restart the swqp service
command: /etc/init.d/dphys-swapfile restart
when: is_debian
- name: Add rpi rootfs resizing service
template: src={{ item.src }}
dest={{ item.dest }}
owner=root
group=root
mode={{ item.mode }}
with_items:
- { src: 'xsce-rpi-max-rootfs.sh', dest: '/usr/sbin/xsce-rpi-max-rootfs.sh', mode: '0755'}
- { src: 'xsce-rpi-root-resize.service', dest: '/etc/systemd/system/xsce-rpi-root-resize.service', mode: '0644'}
- name: Enable rootfs resizing service
service: name=xsce-rpi-root-resize
enabled=yes
- name: Reboot if the config.txt was changed
command: /sbin/reboot
when: rpiconfig.changed
async: 300
poll: 120

34
roles/1-prep/tasks/xsce_ini.yml Executable file
View file

@ -0,0 +1,34 @@
# workaround for fact that auto create does not work on ini_file
- name: Create xsce config file
file: dest='{{ xsce_config_file }}'
state=touch
- name: Add location section to config file
ini_file: dest='{{ xsce_config_file }}'
section=location
option='{{ item.option }}'
value='{{ item.value }}'
with_items:
- option: 'xsce_base'
value: '{{ xsce_base }}'
- option: 'xsce_dir'
value: '{{ xsce_dir }}'
- name: add version section
ini_file: dest='{{ xsce_config_file }}'
section=version
option='{{ item.option }}'
value='{{ item.value }}'
with_items:
- option: 'distribution'
value: '{{ ansible_distribution }}'
- option: 'arch'
value: '{{ ansible_architecture }}'
- option: 'xsce_branch'
value: '{{ ansible_local["local_facts"]["xsce_branch"] }}'
- option: 'xsce_commit'
value: '{{ ansible_local["local_facts"]["xsce_commit"] }}'
- option: 'install_date'
value: '{{ ansible_date_time["iso8601"] }}'
- option: 'install_xo'
value: '{{ xo_model }}'

View file

@ -0,0 +1,3 @@
# /etc/udev/rules.d/92-rtc-i2c.rules
#
ACTION=="add", SUBSYSTEM=="rtc", ATTRS{hctosys}=="0", RUN+="/usr/sbin/hwclock -s --utc"

View file

@ -0,0 +1,46 @@
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
server 0.fedora.pool.ntp.org iburst
server 1.fedora.pool.ntp.org iburst
server 2.fedora.pool.ntp.org iburst
server 3.fedora.pool.ntp.org iburst
# Ignore stratum in source selection.
stratumweight 0
# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift
# Enable kernel RTC synchronization.
rtcsync
# In first three updates step the system clock instead of slew
# if the adjustment is larger than 10 seconds.
makestep 10 3
# Allow NTP client access from local network.
allow 172.18
# Listen for commands only on localhost.
bindcmdaddress 127.0.0.1
bindcmdaddress ::1
# Serve time even if not synchronized to any NTP server.
local stratum 10
keyfile /etc/chrony.keys
# Specify the key used as password for chronyc.
commandkey 1
# Generate command key if missing.
generatecommandkey
# Disable logging of client accesses.
noclientlog
# Send a message to syslog if a clock adjustment is larger than 0.5 seconds.
logchange 0.5
#log measurements statistics tracking
logdir /var/log/chrony

View file

@ -0,0 +1,65 @@
#!/bin/bash
OS=`grep ^ID= /etc/*release|cut -d= -f2`
OS=${OS//\"/}
# enable this install config to be tracked
if [ ! -f /etc/xsce/uuid ]; then
uuidgen > /etc/xsce/uuid
fi
UUID=`cat /etc/xsce/uuid`
source /etc/xsce/xsce.env
cd $XSCE_DIR
# get current version
BRANCH=`git rev-parse --abbrev-ref HEAD`
COMMIT=`git rev-parse --verify HEAD`
if [ -d /usr/lib64/php ]
then
PHPLIB_DIR=/usr/lib64/php
else
if [ -d /usr/lib/php5 ]; then
PHPLIB_DIR=/usr/lib/php5
else
PHPLIB_DIR=/usr/lib/php
fi
fi
if [ -f /proc/device-tree/mfg-data/MN ]
then
XO_VERSION=`cat /proc/device-tree/mfg-data/MN`
else
XO_VERSION="none"
fi
if [ -f /etc/xsce/xsce.env ]
then
. /etc/xsce/xsce.env
if [ -z $BASE_VERSION ]
then
BASE_VERSION="0"
fi
BASE="$BASE_VERSION"
else
BASE="0"
fi
if [ -f /.preload ]
then
PRELOAD="True"
else
PRELOAD="False"
fi
ANSIBLE_VERSION=$(ansible --version|head -n 1|cut -f 2 -d " ")
cat <<EOF
{"phplib_dir" : "$PHPLIB_DIR",
"xsce_branch" : "$BRANCH",
"xsce_commit" : "$COMMIT",
"xsce_uuid" : "$UUID",
"xo_model" : "$XO_VERSION",
"xsce_base_ver" : "$BASE",
"ansble_version" : "$ANSIBLE_VERSION",
"os" : "$OS",
"xsce_preload" : "$PRELOAD"}
EOF

View file

@ -0,0 +1,24 @@
[xsce-rpmfusion-free-updates]
name=xsce-RPM Fusion for Fedora $releasever - Free - Updates
#baseurl=http://download1.rpmfusion.org/free/fedora/updates/$releasever/$basearch/
mirrorlist=http://mirrors.rpmfusion.org/mirrorlist?repo=free-fedora-updates-released-$releasever&arch=$basearch
enabled=0
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rpmfusion-free-fedora-$releasever
[xsce-rpmfusion-free-updates-debuginfo]
name=xsce-RPM Fusion for Fedora $releasever - Free - Updates Debug
#baseurl=http://download1.rpmfusion.org/free/fedora/updates/$releasever/$basearch/debug/
mirrorlist=http://mirrors.rpmfusion.org/mirrorlist?repo=free-fedora-updates-released-debug-$releasever&arch=$basearch
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rpmfusion-free-fedora-$releasever
[xsce-rpmfusion-free-updates-source]
name=xsce-RPM Fusion for Fedora $releasever - Free - Updates Source
#baseurl=http://download1.rpmfusion.org/free/fedora/updates/$releasever/SRPMS/
mirrorlist=http://mirrors.rpmfusion.org/mirrorlist?repo=free-fedora-updates-released-source-$releasever&arch=$basearch
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rpmfusion-free-fedora-$releasever

View file

@ -0,0 +1,137 @@
#!/bin/bash
# if called w/ parameter, skip the reset, create diagnostic package w/ param as file name
SCRIPTDIR=$(cd `dirname $0` pwd)
diagnose_name=
if [ $# -ne 0 ]; then
basket=$1
diagnose_name=$1
else
basket=netlog.$$
fi
# collect all the network info in one place
mkdir -p /tmp/$basket
cat << EOF > /tmp/script2overview
#!/bin/bash
# generate the body overview part diagnostic package about network
echo "=========================================================="
for f in \$(ls /etc/sysconfig/network-scripts/ifcfg-*|gawk '{printf(" %s",\$1)}'); do
echo
echo \$f
cat \$f
done
echo
echo "=========================================================="
echo ifconfig
ifconfig
echo
echo "=========================================================="
echo ip addr
ip addr
echo
echo "=========================================================="
echo "brctl show"
brctl show
echo
echo "=========================================================="
echo "/etc/resolv.conf"
cat /etc/resolv.conf
echo
echo "=========================================================="
echo "cat /etc/xsce/xsce.ini"
cat /etc/xsce/xsce.ini
echo
echo "=========================================================="
echo "routing table"
netstat -rn
echo
echo "=========================================================="
echo "install log -- last 50 lines"
tail -50 /opt/schoolserver/xsce/xsce-install.log
echo
echo "=========================================================="
echo "xsce-network log -- last 50 lines"
if [ -f /opt/schoolserver/xsce/xsce-network.log ]; then
tail -50 /opt/schoolserver/xsce/xsce-network.log
else
echo no xsce-network.log
fi
echo
echo "=========================================================="
cat /etc/fedora-release | grep 18
if [ \$? -eq 0 ]; then
echo "nmcli conn list"
nmcli conn list 3>&2
else
echo "nmcli conn show"
nmcli conn show 3>&2
fi
echo
echo "=========================================================="
echo nmcli dev wifi list
nmcli dev wifi list
EOF
chmod 755 /tmp/script2overview
/tmp/script2overview > /tmp/$basket/overview
if [ -f /opt/schoolserver/xsce/xsce-network.log ]; then
cp /opt/schoolserver/xsce/xsce-network.log /tmp/$basket
else
touch /tmp/$basket/no_xsce-network.log
fi
if [ -f /etc/sysconfig/xs_domain_name ];then
cp -p /etc/sysconfig/xs_domain_name /tmp/$basket
else
touch /tmp/$basket/xs_domain_name_not_set
fi
if [ -f /etc/sysconfig/xs_lan_device ];then
cp -p /etc/sysconfig/xs_lan_device /tmp/$basket
else
touch /tmp/$basket/xs_lan_device_not_set
fi
if [ -f /etc/sysconfig/xs_wan_device ];then
cp -p /etc/sysconfig/xs_wan_device /tmp/$basket
else
touch /tmp/$basket/xs_wan_device_not_set
fi
ls /etc/NetworkManager/system-connections > /dev/null
if [ $? -eq 0 ]; then
cp -rp /etc/NetworkManager/system-connections /tmp/$basket
fi
cp /etc/sysconfig/network-scripts/ifcfg-* /tmp/$basket
if [ -f /opt/schoolserver/xsce/xsce-network.log ]; then
cp -p /opt/schoolserver/xsce/xsce-network.log /tmp/$basket
fi
mkdir -p /etc/xsce/diagnose/
if [ ! -z $diagnose_name ];then
pushd /tmp > /dev/null
tar czf /etc/xsce/diagnose/$basket.tgz $basket/*
popd > /dev/null
rm -rf /tmp/$basket
exit 0
else
pushd /tmp > /dev/null
tar czf /etc/xsce/diagnose/$basket.tgz $basket/*
popd > /dev/null
rm -rf /tmp/$basket
fi
# clear out all the memory variables and let auto-configure start from scratch
rm -rf /etc/sysconfig/xs_domain_name
rm -rf /etc/sysconfig/xs_lan_device
rm -rf /etc/sysconfig/xs_wan_device
rm -rf /etc/NetworkManager/system-connestions/*
if [ -f /etc/sysconfig/network-scripts/ifcfg-WAN ];then
mv /etc/sysconfig/network-scripts/ifcfg-WAN /root
echo -e "\n\nWAN setup file moved to /root for safekeeping.\n\n"
fi
ls -1 /etc/sysconfig/network-scripts/ifcfg-*|grep -v -e ifcfg-lo
if [ $? -eq 0 ]; then
ls -1 /etc/sysconfig/network-scripts/ifcfg-*|grep -v -e ifcfg-lo|xargs rm
fi
echo -e "\n\nAll Network variables erased. Now run 'xsce-network' to set up the new network configuration.\n\nPlease see /opt/schoolserver/xsce/docs/GETTING_HELP.rst for ways to get help or \nprovide the feedback which will improve XSCE\n\n"

View file

@ -0,0 +1,16 @@
[xsce-extra]
name=xsce-extra
failovermethod=priority
baseurl=http://download.unleashkids.org/xsce/repos/xs-extra/
enabled=1
metadata_expire=1d
gpgcheck=0
[dummy-config]
name=dummy-config
failovermethod=priority
baseurl=http://download.unleashkids.org/xsce/repos/xsce-extra/
enabled=1
metadata_expire=1d
gpgcheck=0

View file

@ -0,0 +1,20 @@
#!/bin/bash -x
# Resize rootfs and its partition on the rpi SD card to maximum size
# To be used by systemd service on boot
# Only resizes if /.resize-rootfs exists
# Assumes root is last partition
# Only works on F22 + where resizepart command exists
# Assumes sd card style partition name like <device>p<partition number>
if [ -f /.resize-rootfs ];then
echo "$0: maximizing rootfs partion"
# Calculate root partition
root_part=`lsblk -aP -o NAME,MOUNTPOINT|grep 'MOUNTPOINT="/"' |awk -F\" '{ print $2 }'`
root_dev=${root_part:0:-2}
root_part_no=${root_part: (-1)}
# Resize partition
parted -s /dev/$root_dev resizepart $root_part_no 100%
resize2fs /dev/$root_part
rm /.resize-rootfs
fi

View file

@ -0,0 +1,12 @@
[Unit]
Description=Root Filesystem Auto-Resizer
[Service]
Environment=TERM=linux
Type=oneshot
ExecStart=/usr/sbin/xsce-rpi-max-rootfs.sh
StandardError=syslog
RemainAfterExit=no
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,7 @@
[xsce-testing]
name=xsce-testing
failovermethod=priority
baseurl=http://download.unleashkids.org/xsce/repos/xsce/testing
enabled=1
metadata_expire=1d
gpgcheck=0

View file

@ -0,0 +1,6 @@
# This is a configuration file for XSCE
# It can sourced in a shell script or read into an application
XSCE_BASE_PATH={{ xsce_base }}
XSCE_DIR={{ xsce_dir }}
OS={{ ansible_distribution }}
WWWROOT={{ doc_root }}

View file

@ -0,0 +1,6 @@
=============
Common README
=============
This role aggregates roles containing packages and a few other tasks that are common to all platforms
and are required before creating a functioning server.

View file

@ -0,0 +1,38 @@
- name: Centos Server specific tasks
command: echo Starting centos.yml
- name: Keep yum cache
ini_file: dest=/etc/yum.conf
section=main
option=keepcache
value=1
- name: Install epel-release for CentOS
package: name={{ item }}
state=present
with_items:
- epel-release
- name: Install XECE repo for CentOS
template: src={{ item }} dest=/etc/yum.repos.d/ owner=root group=root mode=0644
with_items:
- xsce-centos.repo
- li.nux.ro.repo
- name: Disable updating ejabberd on CentOS
shell: sed -i -e '/^enabled=/a exclude=ejabberd' {{ item }}
with_items:
- /etc/yum.repos.d/CentOS-Base.repo
- /etc/yum.repos.d/CentOS-CR.repo
- /etc/yum.repos.d/CentOS-fasttrack.repo
- /etc/yum.repos.d/CentOS-Vault.repo
- name: Disable updating ansible on CentOS
shell: sed -i -e '/^enabled=/a exclude=ansible' {{ item }}
with_items:
- /etc/yum.repos.d/CentOS-Base.repo
- /etc/yum.repos.d/CentOS-CR.repo
- /etc/yum.repos.d/CentOS-fasttrack.repo
- /etc/yum.repos.d/CentOS-Vault.repo
when: ansible_distribution == "CentOS"

View file

@ -0,0 +1,30 @@
- name: Keep yum cache
ini_file: dest=/etc/yum.conf
section=main
option=keepcache
value=1
- name: Install Fedora specifc packages
package: name={{ item }}
state=present
with_items:
- mtd-utils
- name: Install optional exFAT packages for Fedora
shell: yum --enablerepo=rpmfusion-free-updates install exfat-utils fuse-exfat
when: exFAT_enabled == "True"
- name: Disable updating ejabberd on Fedora
shell: sed -i -e '/^enabled=/a exclude=ejabberd' {{ item }}
with_items:
- /etc/yum.repos.d/fedora.repo
- /etc/yum.repos.d/fedora-updates.repo
- /etc/yum.repos.d/fedora-updates-testing.repo
- name: Disable updating ansible on Fedora
shell: sed -i -e '/^enabled=/a exclude=ansible' {{ item }}
with_items:
- /etc/yum.repos.d/fedora.repo
- /etc/yum.repos.d/fedora-updates.repo
- /etc/yum.repos.d/fedora-updates-testing.repo
when: ansible_distribution == "Fedora"

View file

@ -0,0 +1,68 @@
- name: Create /opt/schoolserver/xsce
file: path={{ xsce_dir }}
owner=root
group=root
mode=0755
state=directory
- name: Create /opt/schoolserver/yum-packages
file: path={{ yum_packages_dir }}
owner=root
group=root
mode=0755
state=directory
- name: Create /opt/schoolserver/pip-packages
file: path={{ pip_packages_dir }}
owner=root
group=root
mode=0755
state=directory
- name: Create /opt/schoolserver/downloads
file: path={{ downloads_dir }}
owner=root
group=root
mode=0755
state=directory
- name: Create various library directories
file: path={{ item }}
owner=root
group=root
mode=0755
state=directory
with_items:
- /library/downloads/zims
- /library/downloads/rachel
- /library/working/zims
- /library/working/rachel
- "{{ xsce_zim_path }}/content"
- "{{ xsce_zim_path }}/index"
- "{{ rachel_doc_root }}"
- name: Create directory for common packages
file: path={{ item }}
mode=0755
owner=root
group=root
state=directory
with_items:
- "{{ doc_root }}/common/css"
- "{{ doc_root }}/common/js"
- "{{ doc_root }}/common/fonts"
- "{{ doc_root }}/common/html"
- "{{ doc_root }}/common/images"
- "{{ doc_root }}/common/assets"
- "{{ doc_root }}/common/menu-defs"
- name: Create olpc-scripts directory
file: path={{ item }}
owner=root
group=root
mode=0755
state=directory
with_items:
- /etc/sysconfig/olpc-scripts/
- /etc/sysconfig/olpc-scripts/setup.d/installed/

View file

@ -0,0 +1,44 @@
- include: xsce_ini.yml
# create the directory structure for XSCE
- include: fl.yml
- include: xo.yml
when: xo_model != "none" or osbuilder is defined
- include: centos.yml
when: ansible_distribution == "CentOS"
- include: fedora.yml
when: ansible_distribution == "Fedora"
# the following installs common packages for both debian and fedora
- include: packages.yml
- sysctl: name=net.ipv4.ip_forward value=1 state=present
- sysctl: name=net.ipv4.conf.default.rp_filter value=1 state=present
- sysctl: name=net.ipv4.conf.default.accept_source_route value=0 state=present
- sysctl: name=kernel.sysrq value=1 state=present
- sysctl: name=kernel.core_uses_pid value=1 state=present
- sysctl: name=net.ipv4.tcp_syncookies value=1 state=present
- sysctl: name=kernel.shmmax value=268435456 state=present
# IPv6 disabled
- sysctl: name=net.ipv6.conf.all.disable_ipv6 value=1 state=present
- sysctl: name=net.ipv6.conf.default.disable_ipv6 value=1 state=present
- sysctl: name=net.ipv6.conf.lo.disable_ipv6 value=1 state=present
- name: Set default Timezone
shell: ln -sf /usr/share/zoneinfo/{{ xsce_TZ }} /etc/localtime
when: xsce_TZ is defined and xsce_TZ != ""
- name: Install custom profile file
template: dest=/etc/profile.d/zzz_xsce.sh
src=zzz_xsce.sh
owner=root
mode=0644
backup=no
- include: net_mods.yml
when: not is_debian and not is_F18

View file

@ -0,0 +1,31 @@
- name: Disable systemd-networkd.service
service: name=systemd-networkd.service
enabled=no
when: not is_centos
- name: Mask systemd-networkd.service
shell: 'systemctl mask systemd-networkd'
when: not is_centos
- name: Disable systemd-hostnamed.service
service: name=systemd-hostnamed.service
enabled=no
- name: Disable dbus-org.freedesktop.hostname1.service
service: name=dbus-org.freedesktop.hostname1
enabled=no
- name: Mask dbus-org.freedesktop.hostname1.service
shell: 'systemctl mask dbus-org.freedesktop.hostname1'
- name: Disable network.service
service: name=network
enabled=no
- name: Mask network.service
shell: 'systemctl mask network.service'
# Network Manager starts this if needed
- name: Disable wpa_supplicant
service: name=wpa_supplicant
enabled=no

View file

@ -0,0 +1,118 @@
- name: install yum deps for arm!!!
shell: dnf install -y python-urlgrabber pyxattr yum-metadata-parser
when: ansible_distribution == "Fedora" and ansible_machine == "armv7l" and ansible_distribution_version|int >= 22
- name: install yum from Fedora 23 for arm!!!
shell: dnf install -y https://kojipkgs.fedoraproject.org//packages/yum/3.4.3/506.fc23/noarch/yum-3.4.3-506.fc23.noarch.rpm python-dnf
when: ansible_distribution == "Fedora" and ansible_machine == "armv7l" and ansible_distribution_version|int >= 22
- name: install yum if it has been dropped from our distribution -- Fedora 22 uses dnf!!!
shell: dnf install -y yum
when: ansible_distribution == "Fedora" and ansible_distribution_version|int >= 22 and ansible_machine != "armv7l"
- name: get the createrepo program
package: name=createrepo
state=present
when: ansible_distribution != "Debian"
- name: Create local repo
shell: createrepo {{ yum_packages_dir }}
when: ansible_distribution != "Debian"
- name: Install local repo file.
template: dest=/etc/yum.repos.d/xsce-local.repo
src=local.repo
owner=root
mode=0644
when: ansible_distribution != "Debian"
- name: Install yum packages
package: name={{ item }}
state=present
with_items:
- yum-utils
- createrepo
- wpa_supplicant
- linux-firmware
- syslog
- xml-common
when: ansible_distribution != "Debian"
tags:
- download
- name: Install yum packages for Debian
package: name={{ item }}
state=present
with_items:
- inetutils-syslogd
- wpasupplicant
when: ansible_distribution == "Debian"
tags:
- download
- name: Install common packages
package: name={{ item }}
state=present
with_items:
- acpid
- mlocate
- rsync
- htop
- etckeeper
- python-passlib
- usbmount
- net-tools
- openssh-server
- sudo
- logrotate
- make
- tar
- unzip
- bzip2
- i2c-tools
- bridge-utils
- usbutils
- hostapd
- wget
- openssl #FC 18 does not supply, but pear requires
- gawk
tags:
- download
- name: Update common packages (not debian
package: name={{ item }}
state=latest
with_items:
- NetworkManager
- glibc # CVE-2015-7547
- bash
- iptables
when: ansible_distribution != "Debian"
tags:
- download
- name: Update common packages (debian)
package: name={{ item }}
state=latest
with_items:
- libc6
- bash
- iptables
when: ansible_distribution == "Debian"
tags:
- download
# instuctions state to start with a fully updated system before starting, stop using
# ansible as a crutch for developers not following the directions and taking short-cuts
#- name: If version of Network manager has changed, subsequent nmcli commands will fail,restart now
# service: name=NetworkManager
# state=restarted
# when: not installing
# the above should use a handler - all reboots should wait until all
# mods are preformed
- name: Install optional exFAT packages for CentOS
shell: yum --enablerepo=li-nux-ro install exfat-utils fuse-exfat
when: exFAT_enabled == "True" and ansible_distribution == "CentOS"

135
roles/2-common/tasks/xo.yml Normal file
View file

@ -0,0 +1,135 @@
- name: XO Server specific tasks
command: echo Starting XO.yml
- name: Disable sleep
command: touch /etc/powerd/flags/inhibit-suspend
creates=/etc/powerd/flags/inhibit-suspend
- name: Disable sleep on lid closing
lineinfile: dest=/etc/powerd/powerd.conf
regexp='^config_SLEEP_WHEN_LID_CLOSED'
line='config_SLEEP_WHEN_LID_CLOSED="no"'
state=present
backup=yes
- name: Keep yum cache
ini_file: dest=/etc/yum.conf
section=main
option=keepcache
value=1
when: not installing
- name: Keep docs when installing packages
lineinfile: backup=yes
dest=/etc/rpm/macros.imgcreate
regexp='^%_excludedocs'
state=absent
- name: pre-Install packages
package: name={{ item }}
state=latest
with_items:
- usbmount
- man
- man-db
- man-pages
- name: re-Install packages
shell: yum -y reinstall sed libidn grep which util-linux wget gnupg2 groff gnash yum
when: not osbuilder is defined
- name: Configure networkmanager plugin
ini_file: dest=/etc/NetworkManager/NetworkManager.conf
section=main
option=plugins
value=ifcfg-rh,keyfile
- name: check for modem config file
stat: path=/etc/NetworkManager/system-connections/"Sugar Modem Connection"
register: config
- name: Change failure and interval settings for modem connection
ini_file: dest=/etc/NetworkManager/system-connections/"Sugar Modem Connection"
section=ppp
option={{ item.option }}
value={{ item.value }}
backup=yes
mode=0600
with_items:
- { option: 'lcp-echo-failure', value: '5' }
- { option: 'lcp-echo-interval', value: '30' }
when: config.stat.exists
- name: Create bigger rwtab
lineinfile: backup=yes
dest=/etc/sysconfig/readonly-root
regexp='^RW_OPTIONS'
line='RW_OPTIONS="-o size=4M -o nr_inodes=2048"'
state=present
- name: Remove dhcpd entry from /etc/rwtab
lineinfile: backup=yes
dest=/etc/rwtab
regexp='^empty.*/var/lib/dhcpd'
state=absent
- name: Remove php entry from /etc/rwtab
lineinfile: backup=yes
dest=/etc/rwtab
regexp='^empty.*/var/lib/php'
state=absent
- name: Persist /etc/hosts between reboots
lineinfile: backup=yes
dest=/etc/statetab.d/olpc
regexp='^/etc/hosts'
state=absent
- name: Disable /var/log tmpfs
lineinfile: backup=yes
dest=/etc/fstab
regexp='^varlog.*'
state=absent
- name: Enlarge the /tmp directory so that url_get does not error out
lineinfile: backup=yes
dest=/etc/fstab
regexp='^/tmp*'
line='/tmp /tmp tmpfs rw,size=600m 0 0'
- name: Disable graphical login
file: path=/etc/systemd/system/default.target
src=/lib/systemd/system/multi-user.target
state=link
register: disabled_login
- name: Remove custom profile settings
file: path=/etc/profile.d/zzz_olpc.sh
state=absent
- name: Download substitute software for i386 on FC18 XO1.5
get_url: url="{{ xsce_download_url }}/{{ item }}" dest={{ downloads_dir}}/{{ item }}
with_items:
- hostapd_8188_i386
when: wifi_id == "tplink_WM725M" and xo_model == "XO-1.5" and not {{ use_cache }} and not {{ no_network }}
tags:
- xo
- name: Put the substitute in place
copy: src={{ downloads_dir }}/hostapd_8188_i386
dest=/usr/sbin/hostapd
backup=yes
mode=0775
owner=root
group=root
when: wifi_id == "tplink_WM725M" and xo_model == "XO-1.5"
- name: Reboot system
command: /sbin/reboot
when: disabled_login.changed and not installing
ignore_errors: yes
async: 300
poll: 120

View file

@ -0,0 +1,34 @@
# workaround for fact that auto create does not work on ini_file
- name: Create xsce config file
file: dest='{{ xsce_config_file }}'
state=touch
- name: Add location section to config file
ini_file: dest='{{ xsce_config_file }}'
section=location
option='{{ item.option }}'
value='{{ item.value }}'
with_items:
- option: 'xsce_base'
value: '{{ xsce_base }}'
- option: 'xsce_dir'
value: '{{ xsce_dir }}'
- name: add version section
ini_file: dest='{{ xsce_config_file }}'
section=version
option='{{ item.option }}'
value='{{ item.value }}'
with_items:
- option: 'distribution'
value: '{{ ansible_distribution }}'
- option: 'arch'
value: '{{ ansible_architecture }}'
- option: 'xsce_branch'
value: '{{ ansible_local["local_facts"]["xsce_branch"] }}'
- option: 'xsce_commit'
value: '{{ ansible_local["local_facts"]["xsce_commit"] }}'
- option: 'install_date'
value: '{{ ansible_date_time["iso8601"] }}'
- option: 'install_xo'
value: '{{ xo_model }}'

View file

@ -0,0 +1,119 @@
- name: install yum deps for arm!!!
shell: dnf install -y python-urlgrabber pyxattr yum-metadata-parser
when: ansible_distribution == "Fedora" and ansible_machine == "armv7l" and ansible_distribution_version|int >= 22
- name: install yum from Fedora 23 for arm!!!
shell: dnf install -y https://kojipkgs.fedoraproject.org//packages/yum/3.4.3/506.fc23/noarch/yum-3.4.3-506.fc23.noarch.rpm python-dnf
when: ansible_distribution == "Fedora" and ansible_machine == "armv7l" and ansible_distribution_version|int >= 22
- name: install yum if it has been dropped from our distribution -- Fedora 22 uses dnf!!!
shell: dnf install -y yum
when: ansible_distribution == "Fedora" and ansible_distribution_version|int >= 22 and ansible_machine != "armv7l"
- name: get the createrepo program
package: name=createrepo
state=present
when: ansible_distribution != "Debian"
- name: Create local repo
shell: createrepo {{ yum_packages_dir }}
when: ansible_distribution != "Debian"
- name: Install local repo file.
template: dest=/etc/yum.repos.d/xsce-local.repo
src=local.repo
owner=root
mode=0644
when: ansible_distribution != "Debian"
- name: Install yum packages
package: name={{ item }}
state=present
with_items:
- yum-utils
- createrepo
- wpa_supplicant
- linux-firmware
- syslog
- xml-common
when: ansible_distribution != "Debian"
tags:
- download
- name: Install yum packages for Debian
package: name={{ item }}
state=present
with_items:
- inetutils-syslogd
- wpasupplicant
when: ansible_distribution == "Debian"
tags:
- download
- name: Install common packages
package: name={{ item }}
state=present
with_items:
- acpid
- mlocate
- rsync
- htop
- etckeeper
- python-passlib
- usbmount
- net-tools
- openssh-server
- sudo
- logrotate
- make
- tar
- unzip
- bzip2
- i2c-tools
- bridge-utils
- usbutils
- hostapd
- wget
- openssl #FC 18 does not supply, but pear requires
- gawk
- sqlite3
tags:
- download
- name: Update common packages (not debian
package: name={{ item }}
state=latest
with_items:
- NetworkManager
- glibc # CVE-2015-7547
- bash
- iptables
when: ansible_distribution != "Debian"
tags:
- download
- name: Update common packages (debian)
package: name={{ item }}
state=latest
with_items:
- libc6
- bash
- iptables
when: ansible_distribution == "Debian"
tags:
- download
# instuctions state to start with a fully updated system before starting, stop using
# ansible as a crutch for developers not following the directions and taking short-cuts
#- name: If version of Network manager has changed, subsequent nmcli commands will fail,restart now
# service: name=NetworkManager
# state=restarted
# when: not installing
# the above should use a handler - all reboots should wait until all
# mods are preformed
- name: Install optional exFAT packages for CentOS
shell: yum --enablerepo=li-nux-ro install exfat-utils fuse-exfat
when: exFAT_enabled == "True" and ansible_distribution == "CentOS"

View file

@ -0,0 +1,8 @@
[li-nux-ro]
name=RPMs missing in EPEL
baseurl=http://li.nux.ro/download/nux/dextop/el7/x86_64/
enabled=0
gpgcheck=1
gpgkey=http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro

View file

@ -0,0 +1,5 @@
[xsce-local]
name=xsce-local
baseurl=file://{{ yum_packages_dir }}
enabled=1
gpgcheck=0

View file

@ -0,0 +1,7 @@
[xsce-centos]
name=xsce-centos
failovermethod=priority
baseurl=http://download.unleashkids.org/xsce/repos/centos
enabled=1
metadata_expire=1d
gpgcheck=0

View file

@ -0,0 +1,5 @@
# Add back sbin dirs to unprivileged users PATH
case $PATH in
*/sbin/*) ;;
*) PATH=/usr/local/sbin:/usr/sbin:/sbin:$PATH ;;
esac

View file

@ -0,0 +1,12 @@
==================
Base Server README
==================
This role is a place to aggregate roles that are required to create a basic web server.
The functionality here is not packages that are not directly consumed by users, which are in common,
nor specific applications, such as those found in the apps and tools roles.
The difference between this aggregate and server-options is that the roles here are required.
Eventually a graphical configuration console will be added here.

View file

@ -0,0 +1,4 @@
dependencies:
- { role: httpd, tags: ['services','httpd','base'] }
- { role: xsce-admin, tags: ['services','xsce-admin','base'] }
- { role: network, tags: ['services','base','network'], when: not gui_desired_network_role is defined }

View file

@ -0,0 +1,41 @@
- name: Make sure there is a content directory
file: dest={{ doc_root }}/local_content
state=directory
- name: Set up to issue warning if xsce-admin password is still default
template: src=profile_ssh_warn.sh
dest=/etc/profile.d/
- name: Is this LXDE?
stat: path=/home/pi/.config/lxsession
register: lx
- name: Do the same if running on raspbian
template: src=lxde_ssh_warn.sh
dest=/home/pi/.config/lxsession/LXDE-pi/
when: lx.stat.isdir is defined and lx.stat.isdir and is_rpi and is_debian
- name: put a autostart line to check for default password in LXDE
lineinfile: line=@/home/pi/.config/lxsession/LXDE-pi/lxde_ssh_warn.sh
dest=/home/pi/.config/lxsession/LXDE-pi/autostart
when: lx.stat.isdir is defined and lx.stat.isdir and is_rpi and is_debian
- name: Base Server Installed
command: echo Base Server Installed
- name: Restart xsce-cmdsrv service
service: name=xsce-cmdsrv
state=restarted
when: not installing
- name: Restart httpd
service: name={{ apache_service }}
state=restarted
when: not installing
# If we got here we're done
- name: Record base gui version
lineinfile: dest=/etc/xsce/xsce.env
regexp='^BASE_VERSION=*'
line='BASE_VERSION="{{ gui_version }}"'
state=present

View file

@ -0,0 +1,22 @@
#!/bin/bash
# credit to the folks at raspberry pi foundatioon
check_hash ()
{
if ! id -u xsce-admin > /dev/null 2>&1 ; then return 0 ; fi
if grep -q "^PasswordAuthentication\s*no" /etc/ssh/sshd_config ; then return 0 ; fi
test -x /usr/bin/mkpasswd || return 0
SHADOW="$(sudo -n grep -E '^xsce-admin:' /etc/shadow 2>/dev/null)"
test -n "${SHADOW}" || return 0
if echo $SHADOW | grep -q "xsce-admin:!" ; then return 0 ; fi
SHADOW_PW=$(echo $SHADOW | cut -d: -f2)
if [ "$SHADOW_PW" != "\$6\$xsce51\$D.IrrEeLBYIuJkGDmi27pZUGOwPFp98qpl3hxMwWV4hXigFGmdSvy3s/j7tn6OnyTTLmlV7SsN0lCUAFzxSop." ]; then return 0 ; fi
if echo "${SHADOW}" | grep -q "${HASH}"; then
zenity --warning --text="SSH is enabled and the default password for the 'xsce-admin' user has not been changed.\nThis is a security risk - please go to the xsce-console and use utilities-> change password to set a new password."
fi
}
if service ssh status | grep -q running; then
check_hash
fi
unset check_hash

View file

@ -0,0 +1,23 @@
#!/bin/bash
# credit to the folks at raspberry pi foundatioon
check_hash ()
{
if ! id -u xsce-admin > /dev/null 2>&1 ; then return 0 ; fi
if grep -q "^PasswordAuthentication\s*no" /etc/ssh/sshd_config ; then return 0 ; fi
test -x /usr/bin/mkpasswd || return 0
SHADOW="$(sudo -n grep -E '^xsce-admin:' /etc/shadow 2>/dev/null)"
test -n "${SHADOW}" || return 0
if echo $SHADOW | grep -q "xsce-admin:!" ; then return 0 ; fi
SHADOW_PW=$(echo $SHADOW | cut -d: -f2)
if [ "$SHADOW_PW" != "\$6\$xsce51\$D.IrrEeLBYIuJkGDmi27pZUGOwPFp98qpl3hxMwWV4hXigFGmdSvy3s/j7tn6OnyTTLmlV7SsN0lCUAFzxSop." ]; then return 0 ; fi
echo
echo "SSH is enabled and the default password for the 'xsce-admin' user is unchanged."
echo "This is a security risk - please login as the 'xsce-admin' user and type 'passwd' to change password."
echo
}
if /usr/sbin/service ssh status | grep -q running; then
check_hash
fi
unset check_hash

View file

@ -0,0 +1,8 @@
=====================
Server Options README
=====================
This role is a place to aggregate roles that may be optionally added to the basic server,
whereas the roles in base-server are required. As in the case of the base-server the
functionality here is not packages that are not directly consumed by users, which are in common,
nor specific applications, such as those found in the apps and tools roles.

View file

@ -0,0 +1,10 @@
dependencies:
- { role: sshd, tags: ['services','sshd','base'] }
- { role: network, tags: ['services','base','network'], when: gui_desired_network_role is defined }
- { role: postgresql, tags: ['services','postgresql','base'], when: postgresql_install }
- { role: authserver, tags: ['services','authserver','base'], when: authserver_install }
- { role: openvpn, tags: ['options','openvpn'], when: openvpn_install }
- { role: samba, tags: ['services','samba','options'], when: samba_install }
- { role: usb-lib, tags: ['services','usb-lib','options'], when: usb_lib_install }
- { role: cups, tags: ['services','cups','options'], when: cups_install }
- { role: menu, tags: ['services','menu','options'], when: iiab_menu_install }

View file

@ -0,0 +1,26 @@
- name: Server Options Installed
command: echo Server Options Installed
- name: Stop postgresql service
command: "/etc/init.d/postgresql stop"
ignore_errors: True
when: postgresql_install and is_debian
- name: Start postgresql service
service: name=postgresql-xs
state=restarted
enabled=yes
when: postgresql_enabled
- name: Stop authserver service
service: name=xs-authserver
state=stopped
enabled=no
when: not authserver_enabled and authserver_install
- name: Start xs-authserver service
service: name=xs-authserver
state=restarted
when: authserver_enabled

View file

@ -0,0 +1,6 @@
==================
XO Services README
==================
This role is a place to aggregate roles that provide XO specific services.

View file

@ -0,0 +1,4 @@
dependencies:
- { role: ejabberd, tags: ['olpc','ejabberd','xo-services'], when: ejabberd_install }
- { role: idmgr, tags: ['olpc','idmgr','xo-services'], when: idmgr_install }
- { role: activity-server, tags: ['olpc','activity-server','xo-services'], when: activity_server_install }

View file

@ -0,0 +1,3 @@
- name: XO Services Installed
command: echo XO Services Installed

View file

@ -0,0 +1,7 @@
===================
Generic Apps README
===================
This role is a place to aggregate roles that install apps of a more generic nature, as opposed to educational or managment.
Content Management Systems or Chat or Wiki applications would go here.

View file

@ -0,0 +1,7 @@
dependencies:
- { role: mysql, tags: ['generic','mysql'], when: mysql_install }
- { role: elgg, tags: ['generic','elgg'], when: elgg_install }
- { role: owncloud, tags: ['generic','owncloud'], when: owncloud_install }
- { role: dokuwiki, tags: ['generic','dokuwiki'], when: dokuwiki_install }
- { role: wordpress, tags: ['generic','wordpress'], when: wordpress_install }
- { role: calibre, tags: ['generic','calibre'], when: calibre_install }

View file

@ -0,0 +1,3 @@
- name: Generic Apps Installed
command: echo Generic Apps Installed

View file

@ -0,0 +1,6 @@
===================================
Educational Apps and Content README
===================================
This role is a place to aggregate roles that provide Educational Content or
are specifically targetted at pedagogical activities.

View file

@ -0,0 +1,9 @@
dependencies:
- { role: moodle, tags: ['olpc','moodle','edu-apps'], when: moodle_install }
- { role: iiab, tags: ['iiab','edu-apps'], when: iiab_install }
- { role: pathagar, tags: ['pathagar','edu-apps'], when: pathagar_install }
- { role: rachel, tags: ['rachel','edu-apps'], when: rachel_install }
- { role: kalite, tags: ['kalite','edu-apps'], when: kalite_install }
- { role: kiwix, tags: ['kiwix','edu-apps'], when: kiwix_install }
- { role: sugarizer, tags: ['sugarizer','edu-apps'], when: sugarizer_install }
- { role: debian_schooltool, tags: ['schooltool','debian_schooltool','edu-apps'], when: debian_schooltool_install and is_debian }

View file

@ -0,0 +1,3 @@
- name: Educational Apps and Content Installed
command: echo Educational Apps and Content Installed

View file

@ -0,0 +1,6 @@
======================================
Assessment and Monitoring Tools README
======================================
This role is a place to aggregate roles that provide tools for Administering and
Monitoring the Server and for Assessing its use and effectiveness.

View file

@ -0,0 +1,10 @@
dependencies:
- { role: sugar-stats, tags: ['olpc','sugar-stats','tools'], when: sugar_stats_install and ansible_distribution != "CentOS" }
# - { role: ajenti, tags: ['services','ajenti','tools'], when: ajenti_install }
- { role: munin, tags: ['services','munin','tools'], when: munin_install }
- { role: monit, tags: ['services','monit','tools'], when: monit_install }
- { role: vnstat, tags: ['services','vnstat','tools'], when: vnstat_install }
- { role: xovis, tags: ['services','xovis','tools'], when: xovis_install and ansible_distribution != "CentOS" }
- { role: phpmyadmin, tags: ['services','phpmyadmin','tools'], when: phpmyadmin_install }
- { role: awstats, tags: ['services','awstats','tools'], when: awstats_install }
- { role: teamviewer, tags: ['services','teamviewer','tools'], when: teamviewer_install }

View file

@ -0,0 +1,3 @@
- name: Assessment and Monitoring Tools Installed
command: echo Assessment and Monitoring Tools Installed

View file

@ -0,0 +1,21 @@
===================
Local Addons README
===================
This role is a place to aggregate roles developed by various contributors or locally developed.
Development
-----------
Create the role you wish to add to the XSCE School Server by following the pattern of another role or any other means.
Packaging
---------
Add your role into the main.yml file in the meta directory of the 7-local-addons role. It will now get installed as part of
the next ansible run.
More Info
---------
Have a look at the docs section of this git repo for more detailed information.

View file

@ -0,0 +1,3 @@
# Add your role to this list and then uncomment dependencies. Adding a tag is handy for testing.
#dependencies:

View file

@ -0,0 +1,3 @@
- name: Addon services installed
command: echo Addon services installed

View file

@ -0,0 +1,139 @@
Serve XO activities from the XS school server
=============================================
XO laptops can update their activities via HTTP, using specially
microformatted html pages to determine what to download.
This package imports XO activities from a USB stick and generates the
correct html to serve them, in as many languages as it knows how,
using the localisation information from the activity bundles. Content
negotiation between Apache and the laptops decides what html is seen.
The URL for this index is http://schoolserver/activities/.
A facility exists to add extra descriptive html to the generated
indexes.
USB import
----------
When a USB drive is inserted, the server looks for a directory in the
root directory called xs-activity-server. In there it expects to find
any number of activity bundles, a file called manifest.sha1, and
optionally a file or files with the suffix ".info". Depending on the
configuration of the school server, a file called "manifest.sha1.sig"
might also be required.
Activity bundles are zip files with the suffix .xo and an internal
layout described at http://wiki.laptop.org/go/Activity_bundles.
The manifest file should contain sha1sums for each activity bundle and
the metadata files, as if you had run
sha1sum *.xo *.info > manifest.sha1
in the directory.
If full XS security is enabled (by the presence of /etc/xs-security-on
-- see the xs-tools documentation), then manifest.sha1.sig should
contain a detached GPG signature for manifest.sha1, signed by a key
that the XS knows. If the school server lacks the /etc/xs-security-on
flag, the manifest.sha1.sig file is ignored.
Multiple languages
------------------
Activities can contain localisation information, which usually
consists of a translated activity name. The localised information is
found in the bundle in a directory like:
SomeWonderful.activity/locale/pt-BR/activity.linfo
where pt-BR is an RFC1788 language code. If any activity contains an
activity.linfo file for a language, then an index is generated. The
server has templates for indexes in some languages (currently Spanish
and English); for other languages the indexes will be in English
except for the localised names.
These index files are saved with names like
activities/index.html.zh-es. You can choose to look at them directly
that way, or let your browser decide which one is best for you by
visiting activities/index.html.
If some activities lack localised information for a multi-part
language code, the index will include information that exists for the
corresponding single part code, before defaulting to English. For
example, a zh-CN page will include zh localisation if need be. (This
may not always be the best result: bn and bn-IN appear to use
different scripts).
Including extra descriptions
----------------------------
The optional .info files in the xs-activation-server directory should
consist of sections in this format:
[com.microsoft.Word]
description = Write replacement, without distraction of collaboration.
[some.other.Something]
description = another description, all on one line.
If a section heading (in square brackets) matches the bundle_id or
service_name of an activity, the description will be displayed on the
generated html page. This information is not used by automatic
updates, but it might assist children in browsing and manually
installing activities. Note: there is no clever localisation here.
Multiple versions
-----------------
Over the course of a server deployment, an activity might be updated
several times. To preserve disk space, only the 4 most recent
versions of an activity are kept. Links to the second, third and
fourth newest versions are presented in the activities html file, but
these do not use the activity microformat and will not be visible to
automated updaters.
To determine which activities are the most recent, the file's modification
times (mtime) are examined. The version number is not considered here.
Note: If you plug in a USB stick with very out-of-date activities they
will be deleted as soon as they get on the server.
HTML microformat
----------------
The microformat is described at
http://wiki.laptop.org/go/Activity_microformat.
Utility script
--------------
/usr/bin/xs-check-activities will print statistics about a directory
of activities to stderr. Its output is not particularly well
formatted or explained, but it is there if you want it.
Files and directories
---------------------
Activities are stored in /library/xs-activity-server/activities, with
the html index being index.html in that directory. Apache is coaxed
into serving this by /etc/httpd/conf.d/xs-activity-server.conf.
Bugs
----
Old versions are only saved if the different versions have different
file names. Most activity bundles have names like 'Maze-4.xo',
'Maze-5.xo' and so on, but some lack the version number in the file
name, so the most recently imported version ends up overwriting the
older ones.
Source
------
This role is based on the xs-activity-server rpm.
http://dev.laptop.org/git/users/martin/xs-activity-server/ v0.4 release

View file

@ -0,0 +1,3 @@
activity_server_enabled: False
activity_server_install: True

View file

@ -0,0 +1,30 @@
#!/usr/bin/python
# Copyright (C) 2008 One Laptop Per Child Association, Inc.
# Licensed under the terms of the GNU GPL v2 or later; see COPYING for details.
#
# written by Douglas Bagnall <douglas@paradise.net.nz>
"""This script reads activity.info from bundle files and reports on
their quality.
"""
import xs_activities
import sys, os
xs_activities.USE_STDERR = True
show_all = '--show-all' in sys.argv
if show_all:
sys.argv.remove('--show-all')
try:
directory = sys.argv[1]
os.stat(directory)
except (IndexError, OSError):
print __doc__
print "USAGE: %s DIRECTORY" % sys.argv[0]
sys.exit(1)
xs_activities.check_all_bundles(directory, show_all)

View file

@ -0,0 +1,93 @@
#!/usr/bin/python
# Copyright (C) 2008 One Laptop Per Child Association, Inc.
# Licensed under the terms of the GNU GPL v2 or later; see COPYING for details.
#
# written by Douglas Bagnall <douglas@paradise.net.nz>
"""Read activity.info files from a directory full of activity bundles
and write an appropriate html manifest of the most recent versions.
The manifest uses the OLPC activity microformat:
http://wiki.laptop.org/go/Activity_microformat
This is put in a place where apache can find it, and apache will serve
it and the activities to the laptops. Messages go to /var/log/user.log.
"""
import os
import sys
import shutil
from time import time
import xs_activities
INPUT_DIR = "/library/xs-activity-server/activities"
OUTPUT_LINK = "/library/xs-activity-server/www"
try:
CURRENT_DIR = os.readlink(OUTPUT_LINK)
except OSError:
CURRENT_DIR = None
def create_dir_manifest(dir_path):
manifest = []
os.chdir(dir_path)
for root, dirs, files in os.walk("."):
for filename in files:
if not filename.endswith(".xo") and not filename.endswith(".xol"):
continue
path = os.path.join(root, filename)
s = os.stat(path)
manifest.append((path, s.st_ino))
manifest.sort()
return manifest
def input_changed():
if CURRENT_DIR is None:
return True
input_manifest = create_dir_manifest(INPUT_DIR)
current_manifest = create_dir_manifest(CURRENT_DIR)
return input_manifest != current_manifest
if not input_changed():
# no changes or nothing to do
sys.exit(0)
# create new output dir
OUTPUT_DIR = OUTPUT_LINK + "." + str(time())
os.mkdir(OUTPUT_DIR)
# link in all activities
os.chdir(INPUT_DIR)
for root, dirs, files in os.walk("."):
output_dir = os.path.join(OUTPUT_DIR, root)
if not os.path.isdir(output_dir):
os.makedirs(output_dir)
for filename in files:
if not filename.endswith(".xo") and not filename.endswith(".xol"):
continue
path = os.path.join(root, filename)
output_path = os.path.join(output_dir, filename)
os.link(path, output_path)
# create html index
output_html = os.path.join(output_dir, 'index.html')
xs_activities.htmlise_bundles(output_dir, output_html)
# update symlink atomically
link = OUTPUT_DIR + ".lnk"
os.symlink(OUTPUT_DIR, link)
os.rename(link, OUTPUT_LINK)
# remove old index
if CURRENT_DIR is not None:
shutil.rmtree(CURRENT_DIR, ignore_errors=True)

View file

@ -0,0 +1,10 @@
<div class="olpc-activity-info">
<h2>%(name)s</h2>
%(description)s
<ul>
<li>Identifier: <span class="olpc-activity-id">%(bundle_id)s</span></li>
<li>Version: <span class="olpc-activity-version">%(activity_version)s</span></li>
<li>URL: <span class="olpc-activity-url"><a href="%(bundle_url)s">%(bundle_url)s</a></span></li>
<li style="display: %(show_older_versions)s">Older versions: %(older_versions)s</li>
</ul>
</div>

View file

@ -0,0 +1,7 @@
<html>
<body>
<h1 id="olpc-activity-group-name">Locally available activities</h1>
<p id="olpc-activity-group-desc">These activities are stored on the school server.</p>
%(activities)s
</body>
</html>

View file

@ -0,0 +1,10 @@
<div class="olpc-activity-info">
<h2>%(name)s</h2>
%(description)s
<ul>
<li>Identificador: <span class="olpc-activity-id">%(bundle_id)s</span></li>
<li>Versión: <span class="olpc-activity-version">%(activity_version)s</span></li>
<li>URL: <span class="olpc-activity-url"><a href="%(bundle_url)s">%(bundle_url)s</a></span></li>
<li style="display: %(show_older_versions)s">Versiones anteriores: %(older_versions)s</li>
</ul>
</div>

View file

@ -0,0 +1,8 @@
<!DOCTYPE html>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<body>
<h1 id="olpc-activity-group-name">Actividades disponibles localmente</h1>
<p id="olpc-activity-group-desc">Estas actividades están almacenads en el servidor de la escuela.</p>
%(activities)s
</body>
</html>

View file

@ -0,0 +1,10 @@
<div class="olpc-activity-info">
<h2>%(name)s</h2>
%(description)s
<ul>
<li>Identifiant: <span class="olpc-activity-id">%(bundle_id)s</span></li>
<li>Version: <span class="olpc-activity-version">%(activity_version)s</span></li>
<li>URL: <span class="olpc-activity-url"><a href="%(bundle_url)s">%(bundle_url)s</a></span></li>
<li style="display: %(show_older_versions)s">Anciennes versions: %(older_versions)s</li>
</ul>
</div>

View file

@ -0,0 +1,8 @@
<!DOCTYPE html>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<body>
<h1 id="olpc-activity-group-name">Activités disponibles localement</h1>
<p id="olpc-activity-group-desc">Ces activités sont stockées sur le serveur de lécole.</p>
%(activities)s
</body>
</html>

View file

@ -0,0 +1,10 @@
<div class="olpc-activity-info">
<h2>%(name)s</h2>
%(description)s
<ul>
<li>Pou idantifye: <span class="olpc-activity-id">%(bundle_id)s</span></li>
<li>Vesyon: <span class="olpc-activity-version">%(activity_version)s</span></li>
<li>URL: <span class="olpc-activity-url"><a href="%(bundle_url)s">%(bundle_url)s</a></span></li>
<li style="display: %(show_older_versions)s">Vesyon ansyen: %(older_versions)s</li>
</ul>
</div>

View file

@ -0,0 +1,8 @@
<!DOCTYPE html>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<body>
<h1 id="olpc-activity-group-name">Aktivite ki disponib nan Bwat la</h1>
<p id="olpc-activity-group-desc">Aktivite sa yo disponib sou sit lekòl la.</p>
%(activities)s
</body>
</html>

View file

@ -0,0 +1,10 @@
<html>
<body>
<h1 id="olpc-activity-group-name">Locally available activities</h1>
<p id="olpc-activity-group-desc">These activities are stored on the school server.</p>
<div class="olpc-activity-info">
There are currently no activities. Insert a USB drive with activities on it to add some.
</div>
</body>
</html>

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<body>
<h1 id="olpc-activity-group-name">Activités disponibles localement</h1>
<p id="olpc-activity-group-desc">Ces activités sont stockées sur le serveur de lécole.</p>
<div class="olpc-activity-info">
Il n'y a pas d'activités. Insérez une clé USB avec des activités sur pour ajouter.
</div>
</body>
</html>

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<META http-equiv="Content-Type" content="text/html; charset=UTF-8">
<body>
<h1 id="olpc-activity-group-name">Aktivite ki disponib nan Bwat la</h1>
<p id="olpc-activity-group-desc">Aktivite sa yo disponib sou sit lekòl la.</p>
<div class="olpc-activity-info">
Gen kounye a pa gen okenn aktivite. Antre yon USB ak aktivite sou li ajoute kèk.
</div>
</body>
</html>

View file

@ -0,0 +1,535 @@
# Copyright (C) 2008 One Laptop Per Child Association, Inc.
# Licensed under the terms of the GNU GPL v2 or later; see COPYING for details.
#
# written by Douglas Bagnall <douglas@paradise.net.nz>
"""Functions for processing XO activities, either for indexing and
presentaton to the laptops, or for diagnostics.
"""
import os, sys, shutil
import zipfile
import re
from cStringIO import StringIO
#import traceback
import syslog
from ConfigParser import SafeConfigParser
# we no longer really have a default in that it is set in the conf file
# we assume that we have a lang_template for the default language
TEMPLATE_DIR = '/library/xs-activity-server/lang_templates'
DEFAULT_LANG = 'en'
# how many versions before the latest are worth having around.
KEEP_OLD_VERSIONS = 3
#print to stderr, rathe than syslog?
USE_STDERR=False
REQUIRED_TAGS = ('bundle_id', 'activity_version', 'host_version', 'name', 'license')
OPTIONAL_TAGS = ('show_launcher', 'exec', 'mime_types', 'icon')
#XXX need either icon or show_launcher=no
def log(msg, level=syslog.LOG_NOTICE):
if USE_STDERR:
print >> sys.stderr, msg
else:
syslog.openlog( 'xs-activity-server', 0, syslog.LOG_USER )
syslog.syslog(level, msg)
syslog.closelog()
class BundleError(Exception):
pass
class Bundle(object):
def __init__(self, bundle):
self.linfo = {}
self.zf = zipfile.ZipFile(bundle)
# The activity path will be 'Something.activity/activity/activity.info'
for p in self.zf.namelist():
if p.endswith(self.INFO_PATH):
self.raw_data = read_info_file(self.zf, p, self.INFO_SECTION)
# the file name itself is needed for the URL
self.url = os.path.basename(bundle)
self.mtime = os.stat(bundle).st_mtime
self.name = self.raw_data.get('name')
self.license = self.raw_data.get('license', None)
# child ctor should now call
# _set_bundle_id
# _set_version
# _set_description
def _set_bundle_id(self, id):
if id is None:
raise BundleError("bad bundle: No bundle ID")
self.bundle_id = id
if self.name is None:
self.name = id
def _set_version(self, version):
self.version = version
def _set_description(self, description):
self.description = description
def __cmp__(self, other):
"""Alphabetical sort (locale dependant of course)"""
if self.bundle_id == other.bundle_id:
return cmp(self.version, other.version)
return cmp(self.name, other.name)
def set_older_versions(self, versions):
"""Versions should be a list of (version number, version tuples)"""
self.older_versions = ', '.join('<a href="%s">%s</a>' % (v.url, v.version) for v in versions)
def to_html(self, locale, template=None):
"""Fill in the template with data approriate for the locale."""
if template is None:
template = read_template('activity', locale)
d = {'older_versions': self.older_versions,
'bundle_id': self.bundle_id,
'activity_version': self.version,
'bundle_url': self.url,
'name': self.name,
'description': self.description,
}
d.update(self.linfo.get(locale, {}))
if d['older_versions']:
d['show_older_versions'] = 'inline'
else:
d['show_older_versions'] = 'none'
return template % d
def get_name(self, locale=None):
return self.name
class Content(Bundle):
INFO_PATH = "library/library.info"
INFO_SECTION = "Library"
def __init__(self, bundle):
super(Content, self).__init__(bundle)
d = self.raw_data
# bundle_id is often missing; service name is used instead.
self._set_bundle_id(d.get('global_name', None))
self._set_version(d.get('library_version', 1))
self._set_description(d.get('long_name', ''))
def debug(self, force_recheck=False):
# FIXME: implement debug checking for content bundles
return {}
class Activity(Bundle):
INFO_PATH = "activity/activity.info"
INFO_SECTION = "Activity"
#Activities appear to be looser than RFC3066, using e.g. _ in place of -.
linfo_re = re.compile(r'/locale/([A-Za-z]+[\w-]*)/activity.linfo$')
def __init__(self, bundle):
"""Takes a zipped .xo bundle name, returns a dictionary of its
activity info. Can raise a variety of exceptions, all of
which should indicate the bundle is invalid."""
super(Activity, self).__init__(bundle)
# The locale info will be Something.activity/locale/xx_XX/activity.linfo
for p in self.zf.namelist():
linfo = self.linfo_re.search(p)
if linfo:
lang = canonicalise(linfo.group(1))
self.linfo[lang] = read_info_file(self.zf, p, self.INFO_SECTION)
# Unfortunately the dictionary lacks some information, and
# stores other bits in inconsistent ways.
d = self.raw_data
# bundle_id is often missing; service name is used instead.
self._set_bundle_id(d.get('bundle_id', d.get('service_name')))
self._set_version(d.get('activity_version', 1))
self._set_description(d.get('description', ''))
def debug(self, force_recheck=False):
"""Make a copy of the raw data with added bits so we can work
out what is going on. This is useful for diagnosing problems
with odd activities and composing tut-tut-ing emails to their
authors.
Not used in production."""
if hasattr(self, '_debug_data') and not force_recheck:
return self._debug_data
d = self.raw_data.copy()
correct_forms = {
'name': str.upper,
'activity_version': str.isdigit,
'host_version': str.isdigit,
'bundle_id': re.compile(r'^[\w.]+$').match,
'service_name': re.compile(r'^[\w.]+$').match,
'icon': re.compile(r'^[\S]+$').match,
'exec': str.upper,
'mime_types': re.compile(r'^([\w.+-]+/[\w.+-]+;?)*$').match,
'update_url': re.compile(r'^http://([\w-]+\.?)+(:\d+)?(/[\w~%.-]+)*$').match,
#'update_url': re.compile(r'^$').match,
'show_launcher': re.compile(r'^(yes)|(no)$').match,
'class': re.compile(r'^(\w+.?)+$').match,
'license': str.upper,
#'license': re.compile(r'^GPLv[23]\+?$').match,
}
for k, v in d.items():
if k in correct_forms:
f = correct_forms.get(k, len)
if not f(v):
d['BAD ' + k] = v
rcount = 0
for k in REQUIRED_TAGS:
if k not in d:
d['LACKS %s' % k] = True
rcount += 1
d['MISSING KEYS'] = rcount
for t in OPTIONAL_TAGS:
if t not in d:
d['NO ' + t] = True
if not 'icon' in d and d.get('show_launcher') != 'no':
d['NO icon AND show_launcher'] = True
self._debug_data = d
return d
def get_name(self, locale):
"""Return the best guess at a name for the locale."""
for loc in locale_search_path(locale):
if loc in self.linfo and 'name' in self.linfo[loc]:
return self.linfo[loc]['name']
return super(Activity, self).get_name()
def check_all_bundles(directory, show_all_bundles=False):
"""A verbose debug function."""
all_bundles = []
unique_bundles = {}
counts = {}
# watch for these tags and print out the lists
bad_contents = {}
all_linfo = {}
unique_linfo = {}
linfo_keys = {}
log('Checking all activities in %s\n' % directory)
for f in os.listdir(directory):
if not f.endswith('.xo') and not f.endswith('.xol'):
continue
#log(f)
try:
if f.endswith('.xo'):
bundle = Activity(os.path.join(directory, f))
else:
bundle = Content(os.path.join(directory, f))
except Exception, e:
log("IRREDEEMABLE bundle %-25s (Error: %s)" % (f, e), syslog.LOG_WARNING)
#Clump together bundles of the same ID
x = unique_bundles.setdefault(bundle.bundle_id, [])
x.append(bundle)
all_bundles.append(bundle)
if not show_all_bundles:
#only show the newest one of each set.
bundles = []
for versions in unique_bundles.values():
versions.sort()
bundles.append(versions[-1])
else:
bundles = all_bundles
licenses = {}
for bundle in bundles:
bid = bundle.bundle_id
for k, v in bundle.debug().iteritems():
counts[k] = counts.get(k, 0) + 1
if k.startswith('BAD '):
bc = bad_contents.setdefault(k, {})
bc[bid] = v
for k, v in bundle.linfo.iteritems():
linfo_l = all_linfo.setdefault(k, [])
linfo_l.append(bundle)
for x in v:
linfo_keys[x] = linfo_keys.get(x, 0) + 1
if v['name'] != bundle.name:
linfo_l = unique_linfo.setdefault(k, [])
linfo_l.append(bundle)
if bundle.license:
lic = licenses.setdefault(bundle.license, [])
lic.append(bundle.bundle_id)
citems = counts.items()
rare_keys = [k for k, v in citems if v < 10]
lack_counts = dict((k, v) for k, v in citems if k.startswith('LACKS '))
bad_counts = dict((k, v) for k, v in citems if k.startswith('BAD '))
no_counts = dict((k, v) for k, v in citems if k.startswith('NO '))
tag_counts = dict((k, v) for k, v in citems if k not in lack_counts and
k not in bad_counts and k not in no_counts and k != 'MISSING KEYS')
# flag whether the tag is needed, ok, or not
tag_quality = dict((k, '*') for k in REQUIRED_TAGS)
tag_quality.update((k, '+') for k in OPTIONAL_TAGS)
linfo_counts = dict((k, len(v)) for k, v in all_linfo.iteritems())
linfo_uniq_counts = dict((k, len(v)) for k, v in unique_linfo.iteritems())
log('\nFound: %s bundles\n %s unique bundles' % (len(all_bundles), len(unique_bundles)))
for d, name, d2 in [(tag_counts, '\nattribute counts:', tag_quality),
(lack_counts, '\nmissing required keys:', {}),
(no_counts, '\nunused optional keys:', {}),
(bad_counts, '\nill-formed values:', {}),
(linfo_counts, '\nlinfo counts: total localised', linfo_uniq_counts),
(linfo_keys, '\nlinfo keys:', {})]:
log(name)
counts_reversed = [(v, k) for (k, v) in d.iteritems()]
counts_reversed.sort()
counts_reversed.reverse()
for (k, v) in counts_reversed:
log("%-25s %4s %4s" % (v, k, d2.get(v, '')))
log("\nRare keys:")
for k in rare_keys:
if k.startswith('BAD '):
continue
log(k)
for b in bundles:
v = b.debug().get(k)
if v:
log(' %-25s %s' % (b.bundle_id, v))
log("\nInteresting contents:")
for k, v in bad_contents.iteritems():
log(k)
for x in v.iteritems():
log(' %s: %s' % x)
log("\nInteresting linfo:")
for k in ('pseudo',):
log(k)
for a in all_linfo[k]:
if a in unique_linfo.get(k, []):
log(' * %s (%s vs. %s)' % (a.bundle_id, a.name, a.linfo[k]['name']))
else:
log(' %s (%s)' % (a.bundle_id, a.name))
log("\nLicenses:")
for lic, v in licenses.iteritems():
log("%-20s %s" %(repr(lic), len(v)))
log("\nRare licenses:")
for lic, v in licenses.iteritems():
if len(v) < 3:
log(' %s' % lic)
for x in v:
log(" %s" %(x))
log("\nAlmost valid activities:")
for b in bundles:
d = b.debug()
if d['MISSING KEYS'] == 1:
missing = ', '.join(x for x in d if x.startswith('LACKS'))
bad_values = ', '. join(x for x in d if x.startswith('BAD'))
log("%-20s %s %s" %(b.name, missing, bad_values))
log("\nValid activities (maybe):")
for b in bundles:
d = b.debug()
bid = b.bundle_id
if (d['MISSING KEYS'] == 0 and
bid not in bad_contents['BAD mime_types']):
log("%-20s - %s" %(b.name, bid))
#log(a.raw_data)
def read_info_file(zipfile, path, section):
"""Return a dictionary matching the contents of the config file at
path in zipfile"""
cp = SafeConfigParser()
info = StringIO(zipfile.read(path))
cp.readfp(info)
return dict(cp.items(section))
def canonicalise(lang):
"""Make all equivalent language strings the same.
>>> canonicalise('Zh-cN')
zh-CN
>>> canonicalise('zh_CN')
zh-CN
"""
lang = lang.replace('_', '-').upper()
bits = lang.split('-', 1)
bits[0] = bits[0].lower()
return '-'.join(bits)
def locale_search_path(locale):
"""Find a series of sensible locales to try, including
DEFAULT_LANG. For example 'zh-CN' would become ('zh-CN', 'zh',
'DEFAULT_LANG')."""
#XXX might be better to be storing locale as tuple
if '-' in locale:
return (locale, locale.split('-')[0], DEFAULT_LANG)
return (locale, DEFAULT_LANG)
def read_metadata(bundle_dir):
"""Attempt to read data in a metadata file. Raises expected
exceptions if the metadata file isn't readable. The file should
look something like this:
[org.laptop.Pippy]
description = Succinct description of this activity.
[org.laptop.Develop]
description = Succinct description of this activity.
web_icon = develop.png
"""
md_files = [os.path.join(bundle_dir, x)
for x in os.listdir(bundle_dir) if x.endswith('.info')]
cp = SafeConfigParser()
cp.read(md_files)
metadata = {}
for section in cp.sections():
metadata[section] = dict(x for x in cp.items(section))
return metadata
def htmlise_bundles(bundle_dir, dest_html):
"""Makes a nice html manifest for the bundles in a directory. The
manifest only shows the newest version of each bundle.
"""
#so, we collect up a dictionary of lists, then sort each list on
#the version number to find the newest.
bundles = [os.path.join(bundle_dir, x)
for x in os.listdir(bundle_dir) if x.endswith('.xo') or x.endswith('.xol')]
try:
metadata = read_metadata(bundle_dir)
except Exception, e:
log("had trouble reading metadata: %s" % e)
metadata = {}
all_bundles = {}
for filename in bundles:
try:
if filename.endswith('.xo'):
bundle = Activity(filename)
else:
bundle = Content(filename)
x = all_bundles.setdefault(bundle.bundle_id, [])
x.append((bundle.mtime, bundle))
except Exception, e:
log("Couldn't find good activity/library info in %s (Error: %s)" % (filename, e))
newest = []
# create an index for each language that has a template
# but track any locales in bundles in case we do not have templates for them
locales = [os.path.join(o) for o in os.listdir(TEMPLATE_DIR) if os.path.isdir(os.path.join(TEMPLATE_DIR,o))]
locales_found = set ()
for versions in all_bundles.values():
versions = [x[1] for x in sorted(versions)]
# end of list is the newest; beginning of list might need deleting
latest = versions.pop()
locales_found.update(latest.linfo)
newest.append(latest)
goners = versions[:-KEEP_OLD_VERSIONS]
keepers = versions[-KEEP_OLD_VERSIONS:]
for v in goners:
fn = os.path.join(bundle_dir, v.url)
os.remove(fn)
latest.set_older_versions(keepers)
if latest.bundle_id in metadata:
# we have extra metadata with which to fill out details
# presumably this is mainly human-oriented description.
d = metadata[latest.bundle_id]
for k in ('description', 'name'):
if k in d:
setattr(latest, k, d[k])
log('found locales: %s' % locales)
# assume locales is not empty as we have at least the default language
for locale in locales:
try:
make_html(newest, locale, '%s.%s' % (dest_html, locale))
except Exception, e:
log("Couldn't make page for %s (Error: %s)" % (locale, e), syslog.LOG_WARNING)
# make_varfile(locales, dest_html)- have switched to multiviews, so var not needed
def make_varfile(locales, dest_html):
f = open(dest_html + '.var', 'w')
index = os.path.basename(dest_html)
f.write('URI: %s\n\n' % index)
for locale in locales:
f.write('URI: %s.%s\n' % (index, locale))
f.write('Content-type: text/html; charset=utf-8\n')
f.write('Content-language: %s\n\n' % locale)
# now the default, slightly higher qs
f.write('URI: %s.DEFAULT\n' % index)
f.write('Content-type: text/html; charset=utf-8\n')
f.write('Content-language: en\n\n')
f.close()
def read_template(name, locale):
"""Try to read the locale's template, falling back to the
default."""
#also try containing locales, eg 'zh' for 'zh-CN'
for x in locale_search_path(locale):
try:
f = open(os.path.join(TEMPLATE_DIR, x, name))
break
except (OSError, IOError), e:
#log(str(e))
continue
s = f.read()
f.close()
return s
def make_html(bundles, locale, filename):
"""Write a microformated index for the activities in the appropriate language,
and save it to filename."""
page_tmpl = read_template('page', locale)
act_tmpl = read_template('activity', locale)
#bundles.sort() won't cut it.
schwartzian = [ (x.get_name(locale), x.to_html(locale, act_tmpl)) for x in bundles ]
schwartzian.sort()
s = page_tmpl % {'activities': '\n'.join(x[1] for x in schwartzian)}
if os.path.exists(filename):
shutil.move(filename, filename + '~')
f = open(filename, 'w')
f.write(s)
f.close()

View file

@ -0,0 +1,133 @@
# assume apache in admin group
- name: Create xs-activity-server directory tree
file: path={{ item }}
mode=0755
owner=root
group=admin
state=directory
with_items:
- /library/xs-activity-server
- /library/xs-activity-server/activities
- /library/xs-activity-server/lang_templates
- /library/xs-activity-server/www.0
- /library/xs-activity-server/tmp
# Wish synchronize worked, but it doesn't
- name: Copy language templates
command: rsync -a {{xsce_dir}}/roles/activity-server/files/lang_templates /library/xs-activity-server/
- name: Copy default index files
copy: src={{ item }}
dest=/library/xs-activity-server/www.0
mode=0755
owner=root
group=root
with_fileglob:
- www.0/index.html.*
- name: Point www to www.0 as default
file: src=/library/xs-activity-server/www.0
dest=/library/xs-activity-server/www
owner=root
group=admin
state=link
- name: Chown language templates
file: path=/library/xs-activity-server/lang_templates
mode=0644
owner=root
group=admin
state=directory
recurse=yes
# We should have a var for python site-packages directory
- name: Create xs-activity-server python site-packages directory
file: path=/usr/lib/python2.7/site-packages/xs_activities
mode=0755
owner=root
group=root
state=directory
- name: Install Python module
copy: src=xs_activities/__init__.py
dest=/usr/lib/python2.7/site-packages/xs_activities
mode=0644
owner=root
group=root
- name: Copy scripts to /usr/bin
copy: src={{ item }}
dest=/usr/bin
mode=0755
owner=root
group=root
with_items:
- bin/xs-regenerate-activities
- bin/xs-check-activities
# Do in ansible what was done in /etc/sysconfig/olpc-scripts/setup.d/xs-activity-server script
- name: Copy xs-activity-server config file
template: src=xs-activity-server.conf
dest=/etc/{{ apache_config_dir }}
owner=root
group=root
mode=0644
- name: enable mod_expires for debian
command: a2enmod expires
when: is_debian
- name: create the link which enables the site
file: src=/etc/apache2/sites-available/xs-activity-server.conf
dest=/etc/apache2/sites-enabled/xs-activity-server.conf
state=link
when: activity_server_enabled and is_debian
- name: delete the link which enables the site
file: src=/etc/apache2/sites-available/xs-activity-server.conf
dest=/etc/apache2/sites-enabled/xs-activity-server.conf
state=absent
when: not activity_server_enabled and is_debian
- name: Copy xs-activity-server usbmount file
template: src=usbmount-60-xs-activity-server-installcontent
dest=/etc/usbmount/mount.d
owner=root
group=root
mode=0755
# TODO: Fix multiview so it supports portal language menu
# For it only supports client's language code
# TODO: Upload Activity via web interface
# and figure out what to do with olpc_activities.service
# short term addition of link for upload-activity server
# ln -sf /usr/share/xs-config/cfg/html/top/en/cntr_upl_activity.php {{ doc_root }}/upload_activity.php
- name: Restart httpd
service: name={{ apache_service }}
enabled=yes
state=restarted
- name: add xs-activity-server to service list
ini_file: dest='{{ service_filelist }}'
section=activity-server
option='{{ item.option }}'
value='{{ item.value }}'
with_items:
- option: name
value: "Activity Server"
- option: description
value: "Download an Activity."
- option: path
value: /activities
- option: enabled
value: "{{ xo_services_enabled }}"

View file

@ -0,0 +1,115 @@
#!/bin/bash
# Part of the xs-activity-server package
#
# based on a similarly named script in the xs-rsync package
# by Martin Langhoff <martin@laptop.org>
#
# Adapted for xs-activity-server by Douglas Bagnall
# <douglas@paradise.net.nz>
#
# Copyright: One Laptop per Child
set -e
VERBOSE=yes
MAGIC_DIR=$UM_MOUNTPOINT/xs-activity-server
FINAL_DIR=/library/xs-activity-server/activities
FILES_TO_RM=""
# combined with set -e, error() is called if something fails.
error(){
logger -puser.err -t "xs-activity-server[$$]" "Error at line $(caller)"
[ "$FILES_TO_RM" ] && rm -rf $FILES_TO_RM
}
trap error ERR
# Log a string via the syslog facility.
log()
{
if test $1 != debug || expr "$VERBOSE" : "[yY]" > /dev/null; then
logger -p user.$1 -t "xs-activity-server[$$]" -- "$2"
fi
}
STEPS=7
[ -d $MAGIC_DIR ] || exit 0
log notice 'Found activity install directory';
log notice "[1/$STEPS] Checking whether it has a manifest";
if [ -r $MAGIC_DIR/manifest.sha1 ];then
log notice "[2/$STEPS] Seems to have a manifest";
else
log err "[2/$STEPS] Missing manifest"
exit 1;
fi
## Do we have enough space?
# note: we could use awk {'print $4'} instead of the
# perl regex, but it breaks with long /dev nodes
# such as those from LVMs -which wrap. The regex captures the
# number just left of the number with the percentage sign.
NEED=`du -s -B1M $MAGIC_DIR | awk {'print $1'}`
HAVE=`df -B1M $FINAL_DIR | tail -n1 | \
perl -pe 'm/(\d+)\s+\d+\%/; $_=($1-1);'`
if [ $NEED -gt $HAVE ];then
log err 'Not enough free space in /library for these activities - cancelling';
exit 1;
fi
### Copy it first - as the media is bound to be slow
# - make this atomic by cp'ing to a tmpdir, and mv'ing into place
# to be fail-safe
# - mv aside manifest.sha1 and its sig
# - TODO? we could avoid cp'ing files we already have using
# rsync --copy-dest instead of cp
#
log notice "[3/$STEPS] Copying activities to disk";
TMPDEST=`mktemp -d -p /library/xs-activity-server/tmp`
#make sure the tmp directory goes
FILES_TO_RM="$FILES_TO_RM '$TMPDEST'"
cp --preserve=timestamps $MAGIC_DIR/* $TMPDEST
# In a tmpdir we own, safe from race conditions
# run the checksums...
log notice "[4/$STEPS] Checking the manifest";
# mv the manifest to a different dir
TMPMANIF=`mktemp -d -p /library/xs-activity-server/tmp`
FILES_TO_RM="$FILES_TO_RM '$TMPMANIF'"
mv $TMPDEST/manifest.sha1 $TMPMANIF/
if [ -e $TMPDEST/manifest.sha1.sig ]; then
mv $TMPDEST/manifest.sha1.sig $TMPMANIF/
fi
xs-sum -c $TMPMANIF/manifest.sha1 -d $TMPDEST
#Let syslog know what we're doing
cd $TMPDEST
log notice "found $(ls *.xo |wc -l) activities"
log debug "found these activities: $(ls *.xo)"
cd -
log notice "[5/$STEPS] Copy the directories into place";
#XXX not checking whether this clobbers existing files.
mv $TMPDEST/* $FINAL_DIR
#So, now all the activities are in place, but maybe they're not
#newer than what we have. So xs-regenerate-activities has to work that out.
log notice "[6/$STEPS] Regenerating the list of available activities";
/usr/bin/xs-regenerate-activities $FINAL_DIR 2>&1 | logger -p user.debug -t "xs-activity-server[$$]"
log notice "[$STEPS/$STEPS] Finished - XOs can now update activities.";
rm -fr $FILES_TO_RM

View file

@ -0,0 +1,30 @@
# xs-activity-server
#
# Copyright: On Laptop per Child
# GPL v2
# written by Douglas Bagnall <douglas@paradise.net.nz>
#
# This belongs in the apache conf.d directory.
# (probably /etc/httpd/conf.d/)
Alias /activities /library/xs-activity-server/www
<Directory /library/xs-activity-server/www >
# Languages are set in 010-xsce.conf
ExpiresActive On
ExpiresDefault now
Options +MultiViews
Require all granted
#NOTE: an index.html.var file is generated, which ought to make
# MultiViews redundant (by my reading) but it doesn't seem to
# work. Someone could look at that sometime.
</Directory>
#<Directory /activities >
# ExpiresActive On
# ExpiresDefault now
#</Directory>
#<Location /activities >
# ExpiresActive On
# ExpiresDefault now
#</Location>

View file

@ -0,0 +1,5 @@
---
- name: restart ajenti service
service: name=ajenti
enabled=yes
state=restarted

View file

@ -0,0 +1,14 @@
- name: download wondershaper ajenti plugin
pip: name="{{ xsce_download_url }}"/ajenti-plugin-wondershaper-0.3.tar.gz
extra_args="--download {{ pip_packages_dir }}"
when: not {{ use_cache }} and not {{ no_network }}
tags:
- download2
- name: install wondershaper from local download directory
pip: name=ajenti-plugin-wondershaper
extra_args="--no-index --find-links=file://{{ pip_packages_dir }}"
# notify:
# - restart ajenti service

View file

@ -0,0 +1,79 @@
- name: Install python-pip package
package: name=python-pip
state=present
tags:
- download
- name: Install required libraries
package: name={{ item.pkg }}
state=present
with_items:
- pkg: python-imaging
- pkg: python-devel
- pkg: libxslt-devel
- pkg: pyOpenSSL
- pkg: python-daemon
- pkg: gcc
tags:
- download
- name: download ajenti from our repo
pip: name="{{ xsce_download_url }}"/ajenti-0.99.34-patched5.tar.gz
extra_args="--download {{ pip_packages_dir }}"
when: not {{ use_cache }} and not {{ no_network }}
tags:
- download2
- name: install ajenti from local download directory
pip: name=ajenti
extra_args="--no-index --find-links=file://{{ pip_packages_dir }}"
# notify:
# - restart ajenti service
- name: download python-catcher
pip: name=python-catcher version=0.1.3
extra_args="--download {{ pip_packages_dir }}"
when: not {{ use_cache }} and not {{ no_network }}
tags:
- download2
- name: install python-catcher from local download directory
pip: name=python-catcher
extra_args="--no-index --find-links=file://{{ pip_packages_dir }}/python-catcher"
- name: change default port
lineinfile: backup=yes
dest=/etc/ajenti/config.json
state=present
backrefs=yes
regexp='"port":\s*[0-9]{1,5}'
line='"port":9990'
- name: exe permission to ajenti
file: path=/etc/rc.d/init.d/ajenti
mode=0744
state=file
- include: ajenti-wondershaper.yml
when: 'xsce_lan_iface != ""'
# handler doesn't fire
- name: restart ajenti service
service: name=ajenti
enabled=yes
state=restarted
when: ajenti_enabled
- name: Add ajenti to service list
ini_file: dest='{{ service_filelist }}'
section=ajenti
option='{{ item.option }}'
value='{{ item.value }}'
with_items:
- option: name
value: ajenti
- option: description
value: "Ajenti is a client server systems administration tool controlled by a web browser"
- option: enabled
value: "{{ ajenti_enabled }}"

View file

@ -0,0 +1,72 @@
- name: check pip is installed
package: name=python-pip
state=present
tags:
- download
- name: download xs-authserver from pypi
pip: name=xs-authserver
extra_args="--download {{ pip_packages_dir }}"
when: not {{ use_cache }} and not {{ no_network }}
tags:
- download2
- name: install xs-authserver from pypi
pip: name=xs-authserver
extra_args="--no-index --find-links=file://{{ pip_packages_dir }}"
- name: install gunicorn
package: name=python-gunicorn
state=present
tags:
- download
- name: Configure xs-authserver
template: backup=yes
src={{ item.src }}
dest={{ item.dest }}
owner=root
group=root
mode={{ item.mode }}
with_items:
- src: xs-authserver.env.j2
dest: /etc/sysconfig/xs-authserver
mode: 0644
- src: xs-authserver.service.j2
dest: /etc/systemd/system/xs-authserver.service
mode: 0644
- name: create database folder
file: state=directory
path=/var/lib/xs-authserver/
owner=root
group=root
mode=0644
- name: init database
command: xs-authserverctl initdb
ignore_errors: yes
environment:
XS_AUTHSERVER_DATABASE: /var/lib/xs-authserver/data.db
- name: Enable xs-authserver service
service: name=xs-authserver
enabled=yes
when: authserver_enabled
- name: add xs-authserver to service list
ini_file: dest='{{ service_filelist }}'
section=xs-authserver
option='{{ item.option }}'
value='{{ item.value }}'
with_items:
- option: name
value: XS-authserver
- option: description
value: '"xs-authserver implements a seamless web authentication service
using XO laptop registration capabilities. It is heavily inspired
by the Moodle OLPC-XS authentication plugin"'
- option: port
value: 5000
- option: path
value: /

View file

@ -0,0 +1,2 @@
XS_AUTHSERVER_OLPC_XS_DB='/home/idmgr/identity.db'
XS_AUTHSERVER_DATABASE='/var/lib/xs-authserver/data.db'

View file

@ -0,0 +1,10 @@
[Unit]
Description=XS authentication server
After={{ apache_service }}.service local-fs.target
[Service]
EnvironmentFile=-/etc/sysconfig/xs-authserver
ExecStart=/usr/bin/gunicorn -n xs-authserver xs_authserver:app -b 0.0.0.0:5000
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,3 @@
awstats_install: True
awstats_enabled: False
awstats_data_dir: /library/awstats/

View file

@ -0,0 +1,99 @@
- name: Install awstats package
package: name={{ item }}
state=present
with_items:
- awstats
- pwauth
- openssl
tags:
- download
- name: Install awstats package
package: name={{ item }}
state=present
with_items:
- libapache2-mod-authnz-external
- apache2-utils
when: is_debian
tags:
- download
- name: enable cgi execution
command: a2enmod cgi
when: is_debian
- name: Create directory for awstat to use as intermediate summary storage
file: path={{ item }}
mode=0750
owner={{ apache_data }}
group={{ apache_data }}
state=directory
force=true
with_items:
- "{{ awstats_data_dir }}"
- "{{ apache_log_dir }}"
- name: Install the Apache config for Advanced Web Statistics
template: src=apache.conf
dest=/etc/{{ apache_config_dir }}/awstats.conf
owner=root
group=root
mode=0644
when: awstats_enabled and is_debian
- name: Install the Apache config for Advanced Web Statistics
template: src=apache-awstats.conf
dest=/etc/{{ apache_config_dir }}/awstats.conf
owner=root
group=root
mode=0644
when: awstats_enabled and not is_debian
- name: make sure logrotate does not make logs unreadable
template: src=logrotate.d.apache2
dest=/etc/logrotate.d/apache2
when: is_debian
- name: See if awstats package installed a config file
stat: path=/etc/awstats/awstats.conf
register: awstats
- debug: var=awstats
- name: If there was a config file installed by package, move it aside
command: mv /etc/awstats/awstats.conf /etc/awstats/awstats.conf.dist
when: awstats.stat.islnk is defined and not awstats.stat.islnk
- name: Enable Awstats
file: src=/etc/apache2/sites-available/awstats.conf
path=/etc/apache2/sites-enabled/awstats.conf
state=link
when: awstats_enabled and is_debian
- name: Disable Awstats
file: path=/etc/apache2/sites-enabled/awstats.conf
state=absent
when: not awstats_enabled and is_debian
- name: Install the awstats config for Advanced Web Statistics
template: src=awstats.schoolserver.conf.j2
dest=/etc/awstats/awstats.schoolserver.conf
owner=root
group=root
mode=0644
when: awstats_enabled
- name: Create a symbolic link to use when access is by ip address
file: src=/etc/awstats/awstats.schoolserver.conf
dest=/etc/awstats/awstats.conf
state=link
when: awstats_enabled
- name: On first enabling of awstats, summarize httpd logs up to now
shell: /bin/perl /usr/share/awstats/wwwroot/cgi-bin/awstats.pl -config=schoolserver -update
when: awstats_enabled and not is_debian
- name: On first enabling of awstats, summarize httpd logs up to now
shell: /usr/bin/perl /usr/lib/cgi-bin/awstats.pl -config=schoolserver -update
when: awstats_enabled and is_debian

View file

@ -0,0 +1,17 @@
- include: install.yml
when: awstats_install
- name: Add awstats to service list
ini_file: dest='{{ service_filelist }}'
section=awstats
option='{{ item.option }}'
value='{{ item.value }}'
with_items:
- option: name
value: awstats
- option: description
value: '"Awstats is Advanced Web Statistics package written in perl which generates static or dynamic html summaries based upon web server logs"'
- option: installed
value: "{{ awstats_install }}"
- option: enabled
value: "{{ awstats_enabled }}"

View file

@ -0,0 +1,39 @@
#
# Content of this file, with correct values, can be automatically added to
# your Apache server by using the AWStats configure.pl tool.
#
# If using Windows and Perl ActiveStat, this is to enable Perl script as CGI.
#ScriptInterpreterSource registry
#
# Directives to add to your Apache conf file to allow use of AWStats as a CGI.
# Note that path "/usr/share/awstats/" must reflect your AWStats install path.
#
Alias /awstatsclasses "/usr/share/awstats/wwwroot/classes/"
Alias /awstatscss "/usr/share/awstats/wwwroot/css/"
Alias /awstatsicons "/usr/share/awstats/wwwroot/icon/"
ScriptAlias /awstats/ "/usr/share/awstats/wwwroot/cgi-bin/"
ScriptAlias /awstats "/usr/share/awstats/wwwroot/"
#
# This is to permit URL access to scripts/files in AWStats directory.
#
<Directory "/usr/share/awstats/wwwroot">
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
AllowOverride None
DirectoryIndex awstats.pl
AuthType Basic
AuthName "Admin Console"
AuthBasicProvider external
AuthExternal pwauth
require valid-user
</Directory>
# Additional Perl modules
<IfModule mod_env.c>
SetEnv PERL5LIB /usr/share/awstats/lib:/usr/share/awstats/plugins
</IfModule>

View file

@ -0,0 +1,47 @@
# This provides worldwide access to everything below the directory
# Security concerns:
# * Raw log processing data is accessible too for everyone
# * The directory is by default writable by the httpd daemon, so if
# any PHP, CGI or other script can be tricked into copying or
# symlinking stuff here, you have a looking glass into your server,
# and if stuff can be uploaded to here, you have a public warez site!
ScriptAlias /awstats /usr/lib/cgi-bin
<Directory /usr/share/awstats>
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
AllowOverride None
DirectoryIndex /cgi-bin/awstats.pl
require all granted
</Directory>
# This provides worldwide access to everything below the directory
# Security concerns: none known
<Directory /usr/share/awstats/icon>
Options None
AllowOverride None
require all granted
</Directory>
# This provides worldwide access to everything below the directory
# Security concerns: none known
<Directory /usr/share/java/awstats>
Options FollowSymLinks
AllowOverride None
require all granted
</Directory>
# This provides worldwide access to everything in the directory
# Security concerns: none known
Alias /awstats-icon/ /usr/share/awstats/icon/
# This provides worldwide access to everything in the directory
# Security concerns: none known
Alias /awstatsclasses/ /usr/share/java/awstats/
# This (hopefully) enables _all_ CGI scripts in the default directory
# Security concerns: Are you sure _all_ CGI scripts are safe?
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
<Directory /usr/lib/cgi-bin>
Options +ExecCGI
SetHandler cgi-script
require all granted
</Directory>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
MAILTO=root
*/10 * * * * www-data [ -x /usr/share/awstats/tools/update.sh ] && /usr/share/awstats/tools/update.sh
# Generate static reports:
10 03 * * * www-data [ -x /usr/share/awstats/tools/buildstatic.sh ] && /usr/share/awstats/tools/buildstatic.sh

View file

@ -0,0 +1,20 @@
/var/log/apache2/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 640 root www-data
sharedscripts
postrotate
if /etc/init.d/apache2 status > /dev/null ; then \
/etc/init.d/apache2 reload > /dev/null; \
fi;
endscript
prerotate
if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
run-parts /etc/logrotate.d/httpd-prerotate; \
fi; \
endscript
}

View file

@ -0,0 +1,3 @@
calibre_port: 8010
calibre_dbpath: "/library/calibre"
calibre_src_url: "https://raw.githubusercontent.com/kovidgoyal/calibre/master/setup/linux-installer.py"

View file

@ -0,0 +1,75 @@
- name: Get calibre setup file
# the installer works for intel fedora, and Centos, and deals with dependencies
get_url:
url="{{ calibre_src_url }}"
dest="{{ downloads_dir }}/calibre-installer.py"
mode=0755
tags:
download2
when: ansible_distribution == "CentOS"
- name: Install calibre
shell: "{{ downloads_dir }}/calibre-installer.py >> /dev/null"
args:
creates: /usr/bin/calibre-uninstall
tags:
download2
when: calibre_install and ansible_distribution == 'CentOS'
- name: Install Caibre rpms
# the fedora rpm arm version, though older, takes care of dependencies, and exists
package: name={{ item }}
state=present
with_items:
- calibre
when: calibre_install and ansible_distribution != 'CentOS'
tags: download
- name: Create calibre service(s) and support scripts
template: backup=no
src={{ item.src }}
dest={{ item.dest }}
owner=root
group=root
mode={{ item.mode }}
with_items:
- { src: 'calibre-serve.service.j2', dest: '/etc/systemd/system/calibre-serve.service', mode: '0644'}
- { src: 'calibre.conf', dest: '/etc/{{ apache_config_dir }}', mode: '0644'}
when: calibre_install
- name: Create the link for sites-enabled
file: src=/etc/apache2/sites-available/calibre.conf
dest=/etc/apache2/sites-enabled/calibre.conf
state=link
when: is_debian and calibre_enabled
- name: Enable Calibre server
service: name=calibre-serve
enabled=yes
state=started
#async: 900
#poll: 5
when: calibre_enabled
- name: Disable Calibre server
service: name=calibre-serve
enabled=no
state=stopped
when: not calibre_enabled
- name: Add calibre to service list
ini_file: dest='{{ service_filelist }}'
section=calibre
option='{{ item.option }}'
value='{{ item.value }}'
with_items:
- option: description
value: '"Calibre epub book server"'
- option: url
value: "{{ calibre_src_url }}"
- option: database
value: "{{ calibre_dbpath }}"
- option: port
value: "{{ calibre_port }}"
- option: enabled
value: "{{ calibre_enabled }}"

View file

@ -0,0 +1,15 @@
[Unit]
Description=Provides the Calibre Book Server
After=syslog.target network.target local-fs.target
[Service]
Type=forking
PIDFile=/var/run/calibre.pid
{% if is_debian %}
ExecStart=/usr/bin/calibre-server --daemonize --pidfile=/var/run/calibre.pid --port={{ calibre_port }} --with-library={{ calibre_dbpath }}
{% else %}
ExecStart=/bin/calibre-server --daemonize --pidfile=/var/run/calibre.pid --port={{ calibre_port }} --with-library={{ calibre_dbpath }}
{% endif %}
[Install]
TimeoutStartSec=900
WantedBy=multi-user.target

Some files were not shown because too many files have changed in this diff Show more