slapos.core-1.3.10/ 0000755 0000000 0000000 00000000000 12517721227 012530 5 ustar root root slapos.core-1.3.10/README.txt 0000644 0000000 0000000 00000000250 12517720512 014217 0 ustar root root slapos.core
===========
The core of SlapOS.
Contains the SLAP library, and the slapgrid, slapformat, slapproxy tools.
For more information, see http://www.slapos.org.
slapos.core-1.3.10/slapos.core.egg-info/ 0000755 0000000 0000000 00000000000 12517721227 016452 5 ustar root root slapos.core-1.3.10/slapos.core.egg-info/top_level.txt 0000644 0000000 0000000 00000000007 12517721227 021201 0 ustar root root slapos
slapos.core-1.3.10/slapos.core.egg-info/requires.txt 0000644 0000000 0000000 00000000427 12517721227 021055 0 ustar root root Flask
lxml
netaddr>=0.7.5
netifaces
setuptools
supervisor
psutil>=2.0.0
xml_marshaller>=0.9.3
zope.interface
zc.buildout
cliff
requests>=2.4.3
uritemplate
[docs]
Sphinx
repoze.sphinx.autointerface
sphinxcontrib.programoutput
[ipython_console]
ipython
[bpython_console]
bpython slapos.core-1.3.10/slapos.core.egg-info/not-zip-safe 0000644 0000000 0000000 00000000001 12517721142 020674 0 ustar root root
slapos.core-1.3.10/slapos.core.egg-info/SOURCES.txt 0000644 0000000 0000000 00000005565 12517721227 020351 0 ustar root root CHANGES.txt
MANIFEST.in
README.txt
setup.cfg
setup.py
slapos/README.console.txt
slapos/README.format.txt
slapos/README.grid.txt
slapos/README.proxy.txt
slapos/README.slap.txt
slapos/__init__.py
slapos/bang.py
slapos/client.py
slapos/format.py
slapos/human.py
slapos/slapos-client.cfg.example
slapos/slapos-proxy.cfg.example
slapos/slapos.cfg.example
slapos/slapos.xsd
slapos/util.py
slapos/version.py
slapos.core.egg-info/PKG-INFO
slapos.core.egg-info/SOURCES.txt
slapos.core.egg-info/dependency_links.txt
slapos.core.egg-info/entry_points.txt
slapos.core.egg-info/namespace_packages.txt
slapos.core.egg-info/not-zip-safe
slapos.core.egg-info/requires.txt
slapos.core.egg-info/top_level.txt
slapos/cli/__init__.py
slapos/cli/bang.py
slapos/cli/boot.py
slapos/cli/cache.py
slapos/cli/collect.py
slapos/cli/command.py
slapos/cli/config.py
slapos/cli/configure_client.py
slapos/cli/console.py
slapos/cli/entry.py
slapos/cli/format.py
slapos/cli/info.py
slapos/cli/list.py
slapos/cli/proxy_show.py
slapos/cli/proxy_start.py
slapos/cli/register.py
slapos/cli/remove.py
slapos/cli/request.py
slapos/cli/slapgrid.py
slapos/cli/supervisorctl.py
slapos/cli/supervisord.py
slapos/cli/supply.py
slapos/cli/coloredlogs/LICENSE.txt
slapos/cli/coloredlogs/__init__.py
slapos/cli/coloredlogs/converter.py
slapos/cli/coloredlogs/demo.py
slapos/cli/configure_local/__init__.py
slapos/collect/README.txt
slapos/collect/__init__.py
slapos/collect/db.py
slapos/collect/entity.py
slapos/collect/reporter.py
slapos/collect/snapshot.py
slapos/collect/temperature/__init__.py
slapos/collect/temperature/heating.py
slapos/grid/SlapObject.py
slapos/grid/__init__.py
slapos/grid/distribution.py
slapos/grid/exception.py
slapos/grid/networkcache.py
slapos/grid/slapgrid.py
slapos/grid/svcbackend.py
slapos/grid/utils.py
slapos/grid/watchdog.py
slapos/grid/zc.buildout-bootstrap.py
slapos/grid/templates/buildout-tail.cfg.in
slapos/grid/templates/group_partition_supervisord.conf.in
slapos/grid/templates/program_partition_supervisord.conf.in
slapos/grid/templates/supervisord.conf.in
slapos/proxy/__init__.py
slapos/proxy/db_version.py
slapos/proxy/schema.sql
slapos/proxy/views.py
slapos/slap/__init__.py
slapos/slap/slap.py
slapos/slap/slap_recipe_mysqldatabase.py.txt
slapos/slap/util.py
slapos/slap/doc/computer_consumption.xsd
slapos/slap/doc/partition_consumption.xsd
slapos/slap/doc/software_instance.xsd
slapos/slap/interface/__init__.py
slapos/slap/interface/slap.py
slapos/tests/__init__.py
slapos/tests/cli.py
slapos/tests/client.py
slapos/tests/collect.py
slapos/tests/configure_local.py
slapos/tests/distribution.py
slapos/tests/interface.py
slapos/tests/slap.py
slapos/tests/slapformat.py
slapos/tests/slapgrid.py
slapos/tests/slapobject.py
slapos/tests/util.py
slapos/tests/pyflakes/__init__.py
slapos/tests/slapmock/__init__.py
slapos/tests/slapmock/requests.py
slapos/tests/slapproxy/__init__.py
slapos/tests/slapproxy/slapos_multimaster.cfg.in slapos.core-1.3.10/slapos.core.egg-info/entry_points.txt 0000644 0000000 0000000 00000002700 12517721227 021747 0 ustar root root [console_scripts]
slapos-watchdog = slapos.grid.watchdog:main
slapos = slapos.cli.entry:main
[slapos.cli]
node format = slapos.cli.format:FormatCommand
supply = slapos.cli.supply:SupplyCommand
node instance = slapos.cli.slapgrid:InstanceCommand
request = slapos.cli.request:RequestCommand
node tail = slapos.cli.supervisorctl:SupervisorctlTailCommand
configure local = slapos.cli.configure_local:ConfigureLocalCommand
node boot = slapos.cli.boot:BootCommand
node software = slapos.cli.slapgrid:SoftwareCommand
proxy show = slapos.cli.proxy_show:ProxyShowCommand
cache lookup = slapos.cli.cache:CacheLookupCommand
node status = slapos.cli.supervisorctl:SupervisorctlStatusCommand
console = slapos.cli.console:ConsoleCommand
node report = slapos.cli.slapgrid:ReportCommand
node stop = slapos.cli.supervisorctl:SupervisorctlStopCommand
node start = slapos.cli.supervisorctl:SupervisorctlStartCommand
node restart = slapos.cli.supervisorctl:SupervisorctlRestartCommand
node register = slapos.cli.register:RegisterCommand
node collect = slapos.cli.collect:CollectCommand
info = slapos.cli.info:InfoCommand
node supervisord = slapos.cli.supervisord:SupervisordCommand
node bang = slapos.cli.bang:BangCommand
list = slapos.cli.list:ListCommand
remove = slapos.cli.remove:RemoveCommand
proxy start = slapos.cli.proxy_start:ProxyStartCommand
node supervisorctl = slapos.cli.supervisorctl:SupervisorctlCommand
configure client = slapos.cli.configure_client:ConfigureClientCommand
slapos.core-1.3.10/slapos.core.egg-info/dependency_links.txt 0000644 0000000 0000000 00000000001 12517721227 022520 0 ustar root root
slapos.core-1.3.10/slapos.core.egg-info/PKG-INFO 0000644 0000000 0000000 00000114610 12517721227 017552 0 ustar root root Metadata-Version: 1.0
Name: slapos.core
Version: 1.3.10
Summary: SlapOS core.
Home-page: http://www.slapos.org
Author: VIFIB
Author-email: UNKNOWN
License: GPLv3
Description: slapos.core
===========
The core of SlapOS.
Contains the SLAP library, and the slapgrid, slapformat, slapproxy tools.
For more information, see http://www.slapos.org.
Changes
=======
1.3.9 (2015-02-20)
------------------
* slapos.format: allow to format additional list of folder for each partition to use as data storage location.
* slapos.format: allow to create tap without bridge (when using option create_tap and tap_gateway_interface), configure ip route with generated ipv4 for tap to access guest vm from host machine.
* slapos.grid: update generated buildout file with information to acess partition data storage folder.
1.3.8 (2015-02-04)
------------------
* slapos proxy: allow to specify/override host/port from command line.
1.3.7 (2015-01-30)
------------------
* slapos.grid: Don't try to process partition if software_release_url is None. Removes noisy errors in log.
* slapos node report: retry several time when removing processes from supervisor.
1.3.6.3 (2015-01-23)
--------------------
* slapos: make forbid_supervisord_automatic_launch generic.
1.3.6.2 (2015-01-22)
--------------------
* slapos.grid.svcbackend: check if watchdog is started before restarting.
1.3.6.1 (2015-01-19)
--------------------
* slapos: allow to use supervisorctl without automatically starting supervisord.
* slapos: Create supervisor configuration when running CLI.
1.3.6 (2015-01-16)
------------------
* supervisord: allow to start with --nodaemon.
* rename : zc.buildout-bootstap.py -> zc.buildout-bootstrap.py.
* update bootstrap.py.
* slapproxy: add missing getComputerPartitionCertificate method
* slapos boot: fix error reporting when ipv6 is not available
1.3.5 (2014-12-03)
------------------
* slapos.grid: do not ALWAYS sleep for promise_timeout. Instead, poll often, and continue if promise finished. This change allows a two-folds speed improvement in processing partitions.
* slapos.format: don't chown recursively Software Releases.
* slapos.util: use find to chown in chownDirectory.
1.3.4 (2014-11-26)
------------------
* slapos.slap hateoas: get 'me' document with no cache.
* slapos.grid: report: fix unbound 'destroyed' variable.
* slapos.slap: fix __getattr__ of product collection so that product.foo works.
* slapos.cli info/list: use raw print instead of logger.
1.3.3 (2014-11-18)
------------------
* slapos.slap/slapos.proxy: Fix regression: requests library ignores empty parameters.
* slapos.proxy: fix slave support (again)
1.3.2 (2014-11-14)
------------------
* slapos.slap: parse ipv6 and adds brackets if missing. Needed for requests, that now NEEDS brackets for ipv6.
* slapos.slap: cast xml from unicode to string if it is unicode before parsing it.
1.3.1 (2014-11-13)
------------------
* slapos.proxy: fix slave support.
1.3.0 (2014-11-13)
------------------
* Introduce slapos list and slapos info CLIs.
* slapos format: fix use_unique_local_address_block feature, and put default to false in configure_local.
1.2.4.1 (2014-10-09)
--------------------
* slapos format: Don't chown partitions.
* slapos format: alter_user is true again by default.
1.2.4 (2014-09-23)
------------------
* slapos.grid: add support for retention_delay.
1.2.3.1 (2014-09-15)
--------------------
* General: Add compatibility with cliff 1.7.0.
* tests: Prevent slap tests to leak its stubs/mocks.
1.2.3 (2014-09-11)
------------------
* slapos.proxy: Add multimaster basic support.
1.2.2 (2014-09-10)
------------------
* slapos.collect: Compress historical logs and fix folder permissions.
1.2.1 (2014-08-21)
------------------
* slapproxy: add automatic migration to new database schema if needed.
1.2.0 (2014-08-18)
------------------
Note: not officially released as egg.
* slapproxy: add correct support for slaves, instance_guid, state.
* slapproxy: add getComputerPartitionStatus dummy support.
* slapproxy: add multi-nodes support
1.1.2 (2014-06-02)
------------------
* Minor fixes
1.1.1 (2014-05-23)
------------------
* Drop legacy commands
* Introduced SlapOS node Collect
1.0.5 (2014-04-29)
------------------
* Fix slapgrid commands return code
* slapos proxy start do not need to be launched as root
1.0.2.1 (2014-01-16)
--------------------
Fixes:
* Add backward compabitility in slap lib with older slapproxy (<1.0.1)
1.0.1 (2014-01-14)
------------------
New features:
* Add configure-local command for standalone slapos [Cedric de Saint Martin/Gabriel Monnerat]
Fixes:
* Fix slapproxy missing _connection_dict [Rafael Monnerat]
1.0.0 (2014-01-01)
------------------
New features:
* slapconsole: Use readline for completion and history. [Jerome Perrin]
* slapos console: support for ipython and bpython [Marco Mariani]
* Initial windows support. [Jondy Zhao]
* Support new/changed parameters in command line tools, defined in documentation. [Marco Mariani]
* Register: support for one-time authentication token. [Marco Mariani]
* New command: "slapos configure client" [Marco Mariani]
* add new "root_check" option in slapos configuration file (true by default) allowing to bypass "am I root" checks in slapos. [Cedric de Saint Martin]
* Add support for getSoftwareReleaseListFromSoftwareProduct() SLAP method. [Cedric de Saint Martin]
* Add support for Software Product in request, supply and console. [Cedric de Saint Martin]
Major Improvements:
* Major refactoring of entry points, clearly defining all possible command line parameters, separating logic from arg/conf parsing and logger setup, sanitizing most parameters, and adding help and documentation for each command. [Marco Mariani]
* Correct handling of common errors: print error message instead of traceback. [Marco Mariani]
* Dramatically speed up slapformat. [Cedric de Saint Martin]
* Remove CONFIG_SITE env var from Buildout environment, fixing support of OpenSuse 12.x. [Cedric de Saint Martin]
* RootSoftwareInstance is now the default software type. [Cedric de Saint Martin]
* Allow to use SlapOS Client for instances deployed in shared SlapOS Nodes. [Cedric de Saint Martin]
Other fixes:
* Refuse to run 'slapos node' commands as non root. [Marco Mariani]
* Register: Replace all reference to vifib by SlapOS Master. [Cedric de Saint Martin]
* Watchdog: won't call bang if bang was already called but problem has not been solved. [Cédric de Saint Martin]
* Slapgrid: avoid spurious empty lines in Popen() stdout/log. [Marco Mariani]
* Slapgrid: Properly include any partition containing any SR informations in the list of partitions to proceed. [Cedric de Saint Martin]
* Slapgrid: Remove the timestamp file after defined periodicity. Fixes odd use cases when an instance failing to process after some time is still considered as valid by the node. [Cedric de Saint Martin]
* Slapgrid: Fix scary but harmless warnings, fix grammar, remove references to ViFiB. [Cedric de Saint Martin, Jérome Perrin, Marco Mariani]
* Slapgrid: Fixes support of Python >= 2.6. [Arnaud Fontaine]
* Slapgrid: Check if SR is upload-blacklisted only if we have upload informations. [Cedric de Saint Martin]
* Slapgrid: override $HOME to be software_path or instance_path. Fix leaking files like /opt/slapgrid/.npm. [Marco Mariani]
* Slapgrid: Always retrieve certificate and key, update files if content changed. Fix "quick&dirty" manual slapos.cfg swaps (change of Node ID). [Marco Mariani]
* Slapformat: Make sure everybody can read slapos configuration directory. [Cedric de Saint Martin]
* Slapformat: Fix support of slapproxy. [Marco Mariani]
* Slapformat: slapos.xml backup: handle corrupted zip files. [Marco Mariani]
* Slapformat: Don't erase shell information for each user, every time. Allows easy debugging. [Cédric de Saint Martin]
0.35.1 (2013-02-18)
-------------------
New features:
* Add ComputerPartition._instance_guid getter in SLAP library. [Cedric de Saint Martin]
* Add ComputerPartition._instance_guid support in slapproxy. [Cedric de Saint Martin]
Fixes:
* Fix link existence check when deploying instance if SR is not correctly installed. This fixes a misleading error. [Cedric de Saint Martin]
* Improve message shown to user when requesting. [Cedric de Saint Martin]
* Raise NotReady when _requested_state doesn't exist when trying to fetch it from getter. [Cedric de Saint Martin]
0.35 (2013-02-08)
-----------------
* slapos: display version number with help. [Marco Mariani]
* slapformat: backup slapos.xml to a zip archive at every change. [Marco Mariani]
* slapformat: Don't check validity of ipv4 when trying to add address that already exists. [Cedric de Saint Martin]
* slapgrid: create and run $MD5/buildout.cfg for eaiser debugging. [Marco Mariani]
* slapgrid: keep running if cp.error() or sr.error() have issues (fixes 20130119-744D94). [Marco Mariani]
* slapgrid does not crash when there are no certificates (fixes #20130121-136C24). [Marco Mariani]
* Add slapproxy-query command. [Marco Mariani]
* Other minor typo / output fixes.
0.34 (2013-01-23)
-----------------
* networkcache: only match major release number in Debian,
fixed platform detection for Ubuntu. [Marco Mariani]
* symlink to software_release in each partition. [Marco Mariani]
* slapos client: Properly expand "~" when giving configuration file location.
[Cedric de Saint Martin]
* slapgrid: stop instances that should be stopped even if buildout and/or
reporting failed. [Cedric de Saint Martin]
* slapgrid: Don't periodically force-process a stopped instance. [Cedric de Saint Martin]
* slapgrid: Handle pid files of slapgrid launched through different entry points.
[Cedric de Saint Martin]
* Watchdog: Bang is called with correct instance certificates. [Cedric Le Ninivin]
* Watchdog: Fix watchdog call. [Cedric le Ninivin]
* Add a symlink of the used software release in each partitions. [Marco Mariani]
* slapformat is verbose by default. [Cedric de Saint Martin]
* slapproxy: Filter by instance_guid, allow computer partition renames
and change of software_type and requested_state. [Marco Mariani]
* slapproxy: Stop instance even if buildout/reporting is wrong. [Cedric de Saint Martin]
* slapproxy: implement softwareInstanceRename method. [Marco Mariani]
* slapproxy: alllow requests to software_type. [Marco Mariani]
* Many other minor fixes. See git diff for details.
0.33.1 (2012-11-05)
-------------------
* Fix "slapos console" argument parsing. [Cedric de Saint Martin]
0.33 (2012-11-02)
-----------------
* Continue to improve new entry points. The following are now functional:
- slapos node format
- slapos node start/stop/restart/tail
- slapos node supervisord/supervisorctl
- slapos node supply
and add basic usage. [Cedric de Saint Martin]
* Add support for "SLAPOS_CONFIGURATION" and SLAPOS_CLIENT_CONFIGURATION
environment variables. (commit c72a53b1) [Cédric de Saint Martin]
* --only_sr also accepts plain text URIs. [Marco Mariani]
0.32.3 (2012-10-15)
-------------------
* slapgrid: Adopt new return value strategy (0=OK, 1=failed, 2=promise failed)
(commit 5d4e1522). [Cedric de Saint Martin]
* slaplib: add requestComputer (commits 6cbe82e0, aafb86eb). [Łukasz Nowak]
* slapgrid: Add stopasgroup and killasgroup to supervisor (commit 36e0ccc0).
[Cedric de Saint Martin]
* slapproxy: don't start in debug mode by default (commit e32259c8).
[Cédric Le Ninivin
* SlapObject: ALWAYS remove tmpdir (commit a652a610). [Cedric de Saint Martin]
0.32.2 (2012-10-11)
-------------------
* slapgrid: Remove default delay, now that SlapOS Master is Fast as Light
(tm). (commit 03a85d6b8) [Cedric de Saint Martin]
* Fix watchdog entry point name, introduced in v0.31. (commit a8651ba12)
[Cedric de Saint Martin]
* slapgrid: Better filter of instances, won't process false positives anymore
(hopefully). (commit ce0a73b41) [Cedric de Saint Martin]
* Various output improvements. [Cedric de Saint Martin]
0.32.1 (2012-10-09)
-------------------
* slapgrid: Make sure error logs are sent to SlapOS master. Finish
implementation began in 0.32. [Cedric de Saint Martin]
* slapgrid: Fix Usage Report in case of not empty partition with no SR.
[Cedric de Saint Martin]
0.32 (2012-10-04)
-----------------
* Introduce new, simpler "slapos" entry point. See documentation for more
informations. Note: some functionnalities of this new entry point don't work
yet or is not as simple as it should be. [Cedric de Saint Martin, Cedric Le
Ninivin]
* Revamped "slapos request" to work like described in documentation. [Cédric
Le Ninivin, Cédric de Saint Martin]
* Rewrote slapgrid logger to always log into stdout. (commits a4d277c881,
5440626dea)[Cédric de Saint Martin]
0.31.2 (2012-10-02)
-------------------
* Update slapproxy behavior: when instance already exist, only update
partition_parameter_kw. (commit 317d5c8e0aee) [Cedric de Saint Martin]
0.31.1 (2012-10-02)
-------------------
* Fixed Watchdog call in slapgrid. [Cédric Le Ninivin]
0.31 (2012-10-02)
-------------------
* Added slapos-watchdog to bang exited and failing serices in instance
in supervisord. (commits 16b2e8b8, 1dade5cd7) [Cédric Le Ninivin]
* Add safety checks before calling SlapOS Master if mandatory instance
members of SLAP classes are not properly set. Will result in less calls to
SlapOS Master in dirty cases. (commits 5097e87c9763, 5fad6316a0f6d,
f2cd014ea8aa) [Cedric de Saint Martin]
* Add "periodicty" functionnality support for instances: if an instance has
not been processed by slapgrid after defined time, process it. (commits
7609fc7a3d, 56e1c7bfbd) [Cedric Le Ninivin]
* slapproxy: Various improvements in slave support (commits 96c6b78b67,
bcac5a397d, fbb680f53b)[Cedric Le Ninivin]
* slapgrid: bulletproof slapgrid-cp: in case one instance is bad, still
processes all other ones. (commits bac94cdb56, 77bc6c75b3d, bd68b88cc3)
[Cedric de Saint Martin]
* Add support for "upload to binary cache" URL blacklist [Cedric de Saint
Martin]
* Request on proxy are identified by requester and name (commit
0c739c3) [Cedric Le Ninivin]
0.30 (2012-09-19)
-----------------
* Add initial "slave instances" support in slapproxy. [Cedric Le Ninivin]
* slapgrid-ur fix: check for partition informations only if we have to
destroy it. [Cedric de Saint Martin]
0.29 (2012-09-18)
-----------------
* buildout: Migrate slap_connection magic instance profile part to
slap-connection, and use variables names separated with '-'. [Cedric de
Saint Martin]
* slapgrid: Add support for instance.cfg instance profiles [Cedric de Saint
Martin]
* slapgrid-ur: much less calls to master. [Cedric de Saint Martin]
0.28.9 (2012-09-18)
-------------------
* slapgrid: Don't process not updated partitions (regression introduced in
0.28.7). [Cedric de Saint Martin]
0.28.8 (2012-09-18)
-------------------
* slapgrid: Don't process free partitions (regression introduced in 0.28.7).
[Cedric de Saint Martin]
0.28.7 (2012-09-14)
-------------------
* slapgrid: --maximal_delay reappeared to be used in special cases. [Cedric
de Saint Martin]
0.28.6 (2012-09-10)
-------------------
* register now use slapos.cfg.example from master. [Cédric Le Ninivin]
0.28.5 (2012-08-23)
-------------------
* Updated slapos.cfg for register [Cédric Le Ninivin]
0.28.4 (2012-08-22)
-------------------
* Fixed egg building.
0.28.3 (2012-08-22)
-------------------
* Avoid artificial tap creation on system check. [Łukasz Nowak]
0.28.2 (2012-08-17)
-------------------
* Resolved path problem in register [Cédric Le Ninivin]
0.28.1 (2012-08-17)
-------------------
* Resolved critical naming conflict
0.28 (2012-08-17)
-----------------
* Introduce "slapos node register" command, that will register computer to
SlapOS Master (vifib.net by default) for you. [Cédric Le Ninivin]
* Set .timestamp in partitions ONLY after slapgrid thinks it's okay (promises,
...). [Cedric de Saint Martin]
* slapgrid-ur: when destroying (not reporting), only care about instances to
destroy, completely ignore others. [Cedric de Saint Martin]
0.27 (2012-08-08)
-----------------
* slapformat: Raise correct error when no IPv6 is available on selected
interface. [Cedric de Saint Martin]
* slapgrid: Introduce --only_sr and --only_cp.
- only_sr filter and force the run of a single SR, and uses url_md5
(folder_id)
- only_cp filter which computer patition, will be runned. it can be a
list, splited by comman (slappartX,slappartY ...) [Rafael Monnerat]
* slapgrid: Cleanup unused option (--usage-report-periodicity). [Cedric de
Saint Martin]
* slapgrid: --develop will work also for Computer Partitions. [Cedric de Saint
Martin]
* slaplib: setConnectionDict won't call Master if parameters haven't changed.
[Cedric de Saint Martin]
0.26.2 (2012-07-09)
-------------------
* Define UTF-8 encoding in SlapOS Node codebase, as defined in PEP-263.
0.26.1 (2012-07-06)
-------------------
* slapgrid-sr: Add --develop option to make it ignore .completed files.
* SLAP library: it is now possible to fetch whole dict of connection
parameters.
* SLAP library: it is now possible to fetch single instance parameter.
* SLAP library: change Computer and ComputerPartition behavior to have proper
caching of computer partition parameters.
0.26 (2012-07-05)
-----------------
* slapformat: no_bridge option becomes 'not create_tap'.
create_tap is true by default. So a bridge is used and tap will be created by
default. [Cedric de Saint Martin]
* Add delay for slapformat. [Cedric Le Ninivin]
* If no software_type is given, use default one (i.e fix "error 500" when
requesting new instance). [Cedric de Saint Martin]
* slapgrid: promise based software release, new api to fetch full computer
information from server. [Yingjie Xu]
* slapproxy: new api to mock full computer information [Yingjie Xu]
* slapgrid: minor fix randomise delay feature. [Yingjie Xu]
* slapgrid: optimise slapgrid-cp, run buildout only if there is an update
on server side. [Yingjie Xu]
* libslap: Allow accessing ServerError. [Vincent Pelletier]
0.25 (2012-05-16)
-----------------
* Fix support for no_bridge option in configuration files for some values:
no_bridge = false was stated as true. [Cedric de Saint Martin]
* Delay a randomized period of time before calling slapgrid. [Yingjie Xu]
* slapformat: Don't require tunctl if no_bridge is set [Leonardo Rochael]
* slapformat: remove monkey patching when creating address so that it doesn't
return false positive. [Cedric de Saint Martin]
* Various: clearer error messages.
0.24 (2012-03-29)
-----------------
* Handles different errors in a user friendly way [Cedric de Saint Martin]
* slapgrid: Supports software destruction. [Łukasz Nowak]
* slap: added support to Supply.supply state parameter (available, destroyed)
[Łukasz Nowak]
0.23 (2012-02-29)
-----------------
* slapgrid : Don't create tarball of sofwtare release when shacache is not
configured. [Yingjie Xu]
0.22 (2012-02-09)
-----------------
* slapformat : Add no-bridge feature. [Cedric de Saint Martin]
* slapgrid : Add binary cache support. [Yingjie Xu]
0.21 (2011-12-23)
-----------------
* slap: Add renaming API. [Antoine Catton]
0.20 (2011-11-24)
-----------------
* slapgrid: Support service-less parttions. [Antoine Catton]
* slapgrid: Avoid gid collision while dropping privileges. [Antoine Catton]
* slapgrid: Drop down network usage during usage reporting. [Łukasz Nowak]
* general: Add sphinx documentation. [Romain Courteaud]
0.19 (2011-11-07)
-----------------
* bang: Executable to be called by being banged computer. [Łukasz Nowak]
0.18 (2011-10-18)
-----------------
* Fix 0.17 release: missing change for slap library. [Łukasz Nowak]
0.17 (2011-10-18)
-----------------
* slap: Avoid request under the hood. [Łukasz Nowak]
* slap: ComputerPartition.bang provided. It allows to update all instances
in tree. [Łukasz Nowak]
* slap: Computer.bang provided. It allows to bang all instances on computer.
[Łukasz Nowak]
0.16 (2011-10-03)
-----------------
* slapgrid: Bugfix for slapgrid introduced in 0.15. [Łukasz Nowak]
0.15 (2011-09-27)
-----------------
* slapgrid: Sanitize environment variables as early as possible. [Arnaud
Fontaine]
* slap: Docstring bugfix. [Sebastien Robin]
* slap: Make request asynchronous call. [Łukasz Nowak]
0.14 (2011-08-31)
-----------------
* slapgrid: Implement SSL based authentication to shadir and shacache.
[Łukasz Nowak]
* slapgrid, slap: Fix usage report packing list generation. [Nicolas Godbert]
0.13 (2011-08-25)
-----------------
* slapgrid: Implement software signing and shacache upload. [Lucas Carvalho]
* slap: Support slave instances [Gabriel Monnerat]
* slapformat: Generate always address for computer [Łukasz Nowak]
* slapgrid: Support promises scripts [Antoine Catton]
* general: slapos.core gets tests. [many contributors]
0.12 (2011-07-15)
-----------------
* Include modifications that should have been included in 0.11.
0.11 (2011-07-15)
-----------------
* Bug fix : slapconsole : shorthand methods request and supply now correctly
return an object. [Cedric de Saint Martin]
0.10 (2011-07-13)
-----------------
* Fix a bug in slapconsole where request and supply shorthand methods
don't accept all needed parameters. [Cedric de Saint Martin]
0.9 (2011-07-11)
----------------
* slapconsole: Simplify usage and use configuration file. You can now
just run slapconsole and type things like "request(kvm, 'mykvm')".
[Cedric de Saint Martin]
* slapformat: Fix issue of bridge not connected with real interface on
Linux >= 2.6.39 [Arnaud Fontaine]
* slapformat: Allow to have IPv6 only interface, with bridge still supporting
local IPv4 stack. [Łukasz Nowak]
0.8 (2011-06-27)
----------------
* slapgrid: Bugfix for temporary extends cache permissions. [Łukasz Nowak]
0.7 (2011-06-27)
----------------
* slapgrid: Fallback to buildout in own search path. [Łukasz Nowak]
0.6 (2011-06-27)
----------------
* slap: Fix bug: state shall be XML encapsulated. [Łukasz Nowak]
0.5 (2011-06-24)
----------------
* slapgrid: Use temporary extends-cache directory in order to make faster
remote profile refresh. [Łukasz Nowak]
0.4 (2011-06-24)
----------------
* general: Polish requirement versions. [Arnaud Fontaine]
* general: Remove libnetworkcache. [Lucas Carvalho]
* slap: Remove not needed method from interface. [Romain Courteaud]
* slap: state parameter is accepted and transmitted to SlapOS master [Łukasz
Nowak]
* slapformat: Implement dry run. [Vincent Pelletier]
* slapgrid: Allow to select any buildout binary used to bootstrap environment.
[Łukasz Nowak]
0.3 (2011-06-14)
----------------
* slap: Implement SLA by filter_kw in OpenOrder.request. [Łukasz Nowak]
* slap: Timeout network operations. [Łukasz Nowak]
* slapformat: Make slapsoft and slapuser* system users. [Kazuhiko Shiozaki]
* slapgrid: Add more tolerance with supervisord. [Łukasz Nowak]
0.2 (2011-06-01)
----------------
* Include required files in distribution [Łukasz Nowak]
0.1 (2011-05-27)
----------------
* Merged slapos.slap, slapos.tool.console, slapos.tool.format,
slapos.tool.grid, slapos.tool.libnetworkcache and slapos.tool.proxy into one
package: slapos.core
console
-------
The slapconsole tool allows to interact with a SlapOS Master throught the SLAP
library.
For more information about SlapOS or slapconsole usages, please go to
http://community.slapos.org.
The slapconsole tool is only a bare Python console with several global variables
defined and initialized.
Initialization and configuration file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Slapconsole allows to automatically connect to a Master using URL and SSL
certificate from given slapos.cfg.
Certificate has to be *USER* certificate, manually obtained from SlapOS master
web interface.
Slapconsole tools reads the given slapos.cfg configuration file and use the
following informations :
* Master URL is read from [slapos] in the "master_url" parameter.
* SSL Certificate is read from [slapconsole] in the "cert_file" parameter.
* SSL Key is read from [slapconsole] in the "key_file" parameter.
See slapos.cfg.example for examples.
Global functions/variables
~~~~~~~~~~~~~~~~~~~~~~~~~~
* "request()" is a shorthand for slap.registerOpenOrder().request() allowing
to request instances.
* "supply()" is a shorthand for slap.registerSupply().supply() allowing to
request software installation.
For more information about those methods, please read the SLAP library
documentation.
* "product" is an instance of slap.SoftwareProductCollection whose only goal is to retrieve
the URL of the best Software Release of a given Software Product as attribute.
for each attribute call, it will retrieve from the SlapOS Master the best
available Software Release URL and return it.
This allows to request instances in a few words, i.e::
request("mykvm", "http://www.url.com/path/to/current/best/known/kvm/software.cfg")
can be simplified into ::
request("mykvm", product.kvm)
* "slap" is an instance of the SLAP library. It is only used for advanced usages.
"slap" instance is obtained by doing ::
slap = slapos.slap.slap()
slap.initializeConnection(config.master_url,
key_file=config.key_file, cert_file=config.cert_file)
Examples
~~~~~~~~
::
>>> # Request instance
>>> request(product.kvm, "myuniquekvm")
>>> # Request instance on specific computer
>>> request(product.kvm, "myotheruniquekvm",
filter_kw={ "computer_guid": "COMP-12345" })
>>> # Request instance, specifying parameters (here nbd_ip and nbd_port)
>>> request(product.kvm, "mythirduniquekvm",
partition_parameter_kw={"nbd_ip":"2a01:e35:2e27:460:e2cb:4eff:fed9:48dc",
"nbd_port":"1024"})
>>> # Request software installation on owned computer
>>> supply(product.kvm, "mycomputer")
>>> # Fetch existing instance status
>>> request(product.kvm, "myuniquekvm").getState()
>>> # Fetch instance information on already launched instance
>>> request(product.kvm, "myuniquekvm").getConnectionParameter("url")
format
======
slapformat is an application to prepare SlapOS ready node (machine).
It "formats" the machine by:
- creating users and groups
- creating bridge interface
- creating needed tap interfaces
- creating needed directories with proper ownership and permissions
In the end special report is generated and information are posted to
configured SlapOS server.
This program shall be only run by root.
Requirements
------------
Linux with IPv6, bridging and tap interface support.
Binaries:
* brctl
* groupadd
* ip
* tunctl
* useradd
grid
====
slapgrid is a client of SLAPos. SLAPos provides support for deploying a SaaS
system in a minute.
Slapgrid allows you to easily deploy instances of softwares based on buildout
profiles.
For more informations about SLAP and SLAPos, please see the SLAP documentation.
Requirements
------------
A working SLAP server with informations about your computer, in order to
retrieve them.
As Vifib servers use IPv6 only, we strongly recommend an IPv6 enabled UNIX
box.
For the same reasons, Python >= 2.6 with development headers is also strongly
recommended (IPv6 support is not complete in previous releases).
For now, gcc and glibc development headers are required to build most software
releases.
Concepts
--------
Here are the fundamental concepts of slapgrid :
A Software Release (SR) is just a software.
A Computer Partition (CP) is an instance of a Software Release.
Imagine you want to install with slapgrid some software and run it. You will
have to install the software as a Software Release, and then instantiate it,
i.e configuring it for your needs, as a Computer Partition.
How it works
------------
When run, slapgrid will authenticate to the SLAP library with a computer_id and
fetch the list of Software Releases to install or remove and Computer
Partitions to start or stop.
Then, it will process each Software Release, and each Computer Partition.
It will also periodically send to SLAP the usage report of each Computer
Partition.
Installation
------------
With easy_install::
$ easy_install slapgrid
slapgrid needs several directories to be created and configured before being
able to run : a software releases directory, and an instances directory with
configured computer partition directory(ies).
You should create for each Computer Partition directory created a specific user
and associate it with its Computer Partition directory. Each Computer Partition
directory should belongs to this specific user, with permissions of 0750.
Usage
-----
slapgrid needs several informations in order to run. You can specify them by
adding arguments to the slapgrid command line, or by putting then in a
configuration file.
Beware : you need a valid computer resource on server side.
Examples
--------
simple example :
Just run slapgrid:
$ slapgrid --instance-root /path/to/instance/root --software-root
/path/to/software_root --master-url https://some.server/some.resource
--computer-id my.computer.id
configuration file example::
[slapgrid]
instance_root = /path/to/instance/root
software_root = /path/to/software/root
master_url = https://slapos.server/slap_service
computer_id = my.computer.id
then run slapgrid::
$ slapgrid --configuration-file = path/to/configuration/file
proxy
=====
Implement minimalist SlapOS Master server without any security, designed to work
only from localhost with one SlapOS Node (a.k.a Computer).
It implements (or should implement) the SLAP API, as currently implemented in
the SlapOS Master (see slaptool.py in Master).
The only behavioral difference from the SlapOS Master is:
When the proxy doesn't find any free partition (and/or in case of slave
instance, any compatible master instance), it will throw a NotFoundError (404).
slap
====
Simple Language for Accounting and Provisioning python library.
Developer note - python version
-------------------------------
This library is used on client (slapgrid) and server side.
Server is using python2.4 and client is using python2.6
Having this in mind, code of this library *have* to work
on python2.4
How it works
------------
The SLAP main server which is in charge of service coordination receives from participating servers the number of computer paritions which are available, the type of resource which a party is ready provide, and request from parties for resources which are needed.
Each participating server is identified by a unique ID and runs a slap-server daemon. This daemon collects from the main server the installation tasks and does the installation of resources, then notifies the main server of completion whenever a resource is configured, installed and available.
The data structure on the main server is the following:
A - Action: an action which can happen to provide a resource or account its usage
CP - Computer Partition: provides a URL to Access a Cloud Resource
RI - Resource Item: describes a resource
CI - Contract Item: describes the contract to attach the DL to (This is unclear still)
R - Resource: describes a type of cloud resource (ex. MySQL Table) is published on slapgrid.org
DL - Delivery Line: Describes an action happening on a resource item on a computer partition
D - Delivery: groups multiple Delivery Lines
Keywords: slapos core
Platform: UNKNOWN
Classifier: Programming Language :: Python
slapos.core-1.3.10/slapos.core.egg-info/namespace_packages.txt 0000644 0000000 0000000 00000000007 12517721227 023002 0 ustar root root slapos
slapos.core-1.3.10/CHANGES.txt 0000644 0000000 0000000 00000055522 12517720512 014346 0 ustar root root Changes
=======
1.3.9 (2015-02-20)
------------------
* slapos.format: allow to format additional list of folder for each partition to use as data storage location.
* slapos.format: allow to create tap without bridge (when using option create_tap and tap_gateway_interface), configure ip route with generated ipv4 for tap to access guest vm from host machine.
* slapos.grid: update generated buildout file with information to acess partition data storage folder.
1.3.8 (2015-02-04)
------------------
* slapos proxy: allow to specify/override host/port from command line.
1.3.7 (2015-01-30)
------------------
* slapos.grid: Don't try to process partition if software_release_url is None. Removes noisy errors in log.
* slapos node report: retry several time when removing processes from supervisor.
1.3.6.3 (2015-01-23)
--------------------
* slapos: make forbid_supervisord_automatic_launch generic.
1.3.6.2 (2015-01-22)
--------------------
* slapos.grid.svcbackend: check if watchdog is started before restarting.
1.3.6.1 (2015-01-19)
--------------------
* slapos: allow to use supervisorctl without automatically starting supervisord.
* slapos: Create supervisor configuration when running CLI.
1.3.6 (2015-01-16)
------------------
* supervisord: allow to start with --nodaemon.
* rename : zc.buildout-bootstap.py -> zc.buildout-bootstrap.py.
* update bootstrap.py.
* slapproxy: add missing getComputerPartitionCertificate method
* slapos boot: fix error reporting when ipv6 is not available
1.3.5 (2014-12-03)
------------------
* slapos.grid: do not ALWAYS sleep for promise_timeout. Instead, poll often, and continue if promise finished. This change allows a two-folds speed improvement in processing partitions.
* slapos.format: don't chown recursively Software Releases.
* slapos.util: use find to chown in chownDirectory.
1.3.4 (2014-11-26)
------------------
* slapos.slap hateoas: get 'me' document with no cache.
* slapos.grid: report: fix unbound 'destroyed' variable.
* slapos.slap: fix __getattr__ of product collection so that product.foo works.
* slapos.cli info/list: use raw print instead of logger.
1.3.3 (2014-11-18)
------------------
* slapos.slap/slapos.proxy: Fix regression: requests library ignores empty parameters.
* slapos.proxy: fix slave support (again)
1.3.2 (2014-11-14)
------------------
* slapos.slap: parse ipv6 and adds brackets if missing. Needed for requests, that now NEEDS brackets for ipv6.
* slapos.slap: cast xml from unicode to string if it is unicode before parsing it.
1.3.1 (2014-11-13)
------------------
* slapos.proxy: fix slave support.
1.3.0 (2014-11-13)
------------------
* Introduce slapos list and slapos info CLIs.
* slapos format: fix use_unique_local_address_block feature, and put default to false in configure_local.
1.2.4.1 (2014-10-09)
--------------------
* slapos format: Don't chown partitions.
* slapos format: alter_user is true again by default.
1.2.4 (2014-09-23)
------------------
* slapos.grid: add support for retention_delay.
1.2.3.1 (2014-09-15)
--------------------
* General: Add compatibility with cliff 1.7.0.
* tests: Prevent slap tests to leak its stubs/mocks.
1.2.3 (2014-09-11)
------------------
* slapos.proxy: Add multimaster basic support.
1.2.2 (2014-09-10)
------------------
* slapos.collect: Compress historical logs and fix folder permissions.
1.2.1 (2014-08-21)
------------------
* slapproxy: add automatic migration to new database schema if needed.
1.2.0 (2014-08-18)
------------------
Note: not officially released as egg.
* slapproxy: add correct support for slaves, instance_guid, state.
* slapproxy: add getComputerPartitionStatus dummy support.
* slapproxy: add multi-nodes support
1.1.2 (2014-06-02)
------------------
* Minor fixes
1.1.1 (2014-05-23)
------------------
* Drop legacy commands
* Introduced SlapOS node Collect
1.0.5 (2014-04-29)
------------------
* Fix slapgrid commands return code
* slapos proxy start do not need to be launched as root
1.0.2.1 (2014-01-16)
--------------------
Fixes:
* Add backward compabitility in slap lib with older slapproxy (<1.0.1)
1.0.1 (2014-01-14)
------------------
New features:
* Add configure-local command for standalone slapos [Cedric de Saint Martin/Gabriel Monnerat]
Fixes:
* Fix slapproxy missing _connection_dict [Rafael Monnerat]
1.0.0 (2014-01-01)
------------------
New features:
* slapconsole: Use readline for completion and history. [Jerome Perrin]
* slapos console: support for ipython and bpython [Marco Mariani]
* Initial windows support. [Jondy Zhao]
* Support new/changed parameters in command line tools, defined in documentation. [Marco Mariani]
* Register: support for one-time authentication token. [Marco Mariani]
* New command: "slapos configure client" [Marco Mariani]
* add new "root_check" option in slapos configuration file (true by default) allowing to bypass "am I root" checks in slapos. [Cedric de Saint Martin]
* Add support for getSoftwareReleaseListFromSoftwareProduct() SLAP method. [Cedric de Saint Martin]
* Add support for Software Product in request, supply and console. [Cedric de Saint Martin]
Major Improvements:
* Major refactoring of entry points, clearly defining all possible command line parameters, separating logic from arg/conf parsing and logger setup, sanitizing most parameters, and adding help and documentation for each command. [Marco Mariani]
* Correct handling of common errors: print error message instead of traceback. [Marco Mariani]
* Dramatically speed up slapformat. [Cedric de Saint Martin]
* Remove CONFIG_SITE env var from Buildout environment, fixing support of OpenSuse 12.x. [Cedric de Saint Martin]
* RootSoftwareInstance is now the default software type. [Cedric de Saint Martin]
* Allow to use SlapOS Client for instances deployed in shared SlapOS Nodes. [Cedric de Saint Martin]
Other fixes:
* Refuse to run 'slapos node' commands as non root. [Marco Mariani]
* Register: Replace all reference to vifib by SlapOS Master. [Cedric de Saint Martin]
* Watchdog: won't call bang if bang was already called but problem has not been solved. [Cédric de Saint Martin]
* Slapgrid: avoid spurious empty lines in Popen() stdout/log. [Marco Mariani]
* Slapgrid: Properly include any partition containing any SR informations in the list of partitions to proceed. [Cedric de Saint Martin]
* Slapgrid: Remove the timestamp file after defined periodicity. Fixes odd use cases when an instance failing to process after some time is still considered as valid by the node. [Cedric de Saint Martin]
* Slapgrid: Fix scary but harmless warnings, fix grammar, remove references to ViFiB. [Cedric de Saint Martin, Jérome Perrin, Marco Mariani]
* Slapgrid: Fixes support of Python >= 2.6. [Arnaud Fontaine]
* Slapgrid: Check if SR is upload-blacklisted only if we have upload informations. [Cedric de Saint Martin]
* Slapgrid: override $HOME to be software_path or instance_path. Fix leaking files like /opt/slapgrid/.npm. [Marco Mariani]
* Slapgrid: Always retrieve certificate and key, update files if content changed. Fix "quick&dirty" manual slapos.cfg swaps (change of Node ID). [Marco Mariani]
* Slapformat: Make sure everybody can read slapos configuration directory. [Cedric de Saint Martin]
* Slapformat: Fix support of slapproxy. [Marco Mariani]
* Slapformat: slapos.xml backup: handle corrupted zip files. [Marco Mariani]
* Slapformat: Don't erase shell information for each user, every time. Allows easy debugging. [Cédric de Saint Martin]
0.35.1 (2013-02-18)
-------------------
New features:
* Add ComputerPartition._instance_guid getter in SLAP library. [Cedric de Saint Martin]
* Add ComputerPartition._instance_guid support in slapproxy. [Cedric de Saint Martin]
Fixes:
* Fix link existence check when deploying instance if SR is not correctly installed. This fixes a misleading error. [Cedric de Saint Martin]
* Improve message shown to user when requesting. [Cedric de Saint Martin]
* Raise NotReady when _requested_state doesn't exist when trying to fetch it from getter. [Cedric de Saint Martin]
0.35 (2013-02-08)
-----------------
* slapos: display version number with help. [Marco Mariani]
* slapformat: backup slapos.xml to a zip archive at every change. [Marco Mariani]
* slapformat: Don't check validity of ipv4 when trying to add address that already exists. [Cedric de Saint Martin]
* slapgrid: create and run $MD5/buildout.cfg for eaiser debugging. [Marco Mariani]
* slapgrid: keep running if cp.error() or sr.error() have issues (fixes 20130119-744D94). [Marco Mariani]
* slapgrid does not crash when there are no certificates (fixes #20130121-136C24). [Marco Mariani]
* Add slapproxy-query command. [Marco Mariani]
* Other minor typo / output fixes.
0.34 (2013-01-23)
-----------------
* networkcache: only match major release number in Debian,
fixed platform detection for Ubuntu. [Marco Mariani]
* symlink to software_release in each partition. [Marco Mariani]
* slapos client: Properly expand "~" when giving configuration file location.
[Cedric de Saint Martin]
* slapgrid: stop instances that should be stopped even if buildout and/or
reporting failed. [Cedric de Saint Martin]
* slapgrid: Don't periodically force-process a stopped instance. [Cedric de Saint Martin]
* slapgrid: Handle pid files of slapgrid launched through different entry points.
[Cedric de Saint Martin]
* Watchdog: Bang is called with correct instance certificates. [Cedric Le Ninivin]
* Watchdog: Fix watchdog call. [Cedric le Ninivin]
* Add a symlink of the used software release in each partitions. [Marco Mariani]
* slapformat is verbose by default. [Cedric de Saint Martin]
* slapproxy: Filter by instance_guid, allow computer partition renames
and change of software_type and requested_state. [Marco Mariani]
* slapproxy: Stop instance even if buildout/reporting is wrong. [Cedric de Saint Martin]
* slapproxy: implement softwareInstanceRename method. [Marco Mariani]
* slapproxy: alllow requests to software_type. [Marco Mariani]
* Many other minor fixes. See git diff for details.
0.33.1 (2012-11-05)
-------------------
* Fix "slapos console" argument parsing. [Cedric de Saint Martin]
0.33 (2012-11-02)
-----------------
* Continue to improve new entry points. The following are now functional:
- slapos node format
- slapos node start/stop/restart/tail
- slapos node supervisord/supervisorctl
- slapos node supply
and add basic usage. [Cedric de Saint Martin]
* Add support for "SLAPOS_CONFIGURATION" and SLAPOS_CLIENT_CONFIGURATION
environment variables. (commit c72a53b1) [Cédric de Saint Martin]
* --only_sr also accepts plain text URIs. [Marco Mariani]
0.32.3 (2012-10-15)
-------------------
* slapgrid: Adopt new return value strategy (0=OK, 1=failed, 2=promise failed)
(commit 5d4e1522). [Cedric de Saint Martin]
* slaplib: add requestComputer (commits 6cbe82e0, aafb86eb). [Łukasz Nowak]
* slapgrid: Add stopasgroup and killasgroup to supervisor (commit 36e0ccc0).
[Cedric de Saint Martin]
* slapproxy: don't start in debug mode by default (commit e32259c8).
[Cédric Le Ninivin
* SlapObject: ALWAYS remove tmpdir (commit a652a610). [Cedric de Saint Martin]
0.32.2 (2012-10-11)
-------------------
* slapgrid: Remove default delay, now that SlapOS Master is Fast as Light
(tm). (commit 03a85d6b8) [Cedric de Saint Martin]
* Fix watchdog entry point name, introduced in v0.31. (commit a8651ba12)
[Cedric de Saint Martin]
* slapgrid: Better filter of instances, won't process false positives anymore
(hopefully). (commit ce0a73b41) [Cedric de Saint Martin]
* Various output improvements. [Cedric de Saint Martin]
0.32.1 (2012-10-09)
-------------------
* slapgrid: Make sure error logs are sent to SlapOS master. Finish
implementation began in 0.32. [Cedric de Saint Martin]
* slapgrid: Fix Usage Report in case of not empty partition with no SR.
[Cedric de Saint Martin]
0.32 (2012-10-04)
-----------------
* Introduce new, simpler "slapos" entry point. See documentation for more
informations. Note: some functionnalities of this new entry point don't work
yet or is not as simple as it should be. [Cedric de Saint Martin, Cedric Le
Ninivin]
* Revamped "slapos request" to work like described in documentation. [Cédric
Le Ninivin, Cédric de Saint Martin]
* Rewrote slapgrid logger to always log into stdout. (commits a4d277c881,
5440626dea)[Cédric de Saint Martin]
0.31.2 (2012-10-02)
-------------------
* Update slapproxy behavior: when instance already exist, only update
partition_parameter_kw. (commit 317d5c8e0aee) [Cedric de Saint Martin]
0.31.1 (2012-10-02)
-------------------
* Fixed Watchdog call in slapgrid. [Cédric Le Ninivin]
0.31 (2012-10-02)
-------------------
* Added slapos-watchdog to bang exited and failing serices in instance
in supervisord. (commits 16b2e8b8, 1dade5cd7) [Cédric Le Ninivin]
* Add safety checks before calling SlapOS Master if mandatory instance
members of SLAP classes are not properly set. Will result in less calls to
SlapOS Master in dirty cases. (commits 5097e87c9763, 5fad6316a0f6d,
f2cd014ea8aa) [Cedric de Saint Martin]
* Add "periodicty" functionnality support for instances: if an instance has
not been processed by slapgrid after defined time, process it. (commits
7609fc7a3d, 56e1c7bfbd) [Cedric Le Ninivin]
* slapproxy: Various improvements in slave support (commits 96c6b78b67,
bcac5a397d, fbb680f53b)[Cedric Le Ninivin]
* slapgrid: bulletproof slapgrid-cp: in case one instance is bad, still
processes all other ones. (commits bac94cdb56, 77bc6c75b3d, bd68b88cc3)
[Cedric de Saint Martin]
* Add support for "upload to binary cache" URL blacklist [Cedric de Saint
Martin]
* Request on proxy are identified by requester and name (commit
0c739c3) [Cedric Le Ninivin]
0.30 (2012-09-19)
-----------------
* Add initial "slave instances" support in slapproxy. [Cedric Le Ninivin]
* slapgrid-ur fix: check for partition informations only if we have to
destroy it. [Cedric de Saint Martin]
0.29 (2012-09-18)
-----------------
* buildout: Migrate slap_connection magic instance profile part to
slap-connection, and use variables names separated with '-'. [Cedric de
Saint Martin]
* slapgrid: Add support for instance.cfg instance profiles [Cedric de Saint
Martin]
* slapgrid-ur: much less calls to master. [Cedric de Saint Martin]
0.28.9 (2012-09-18)
-------------------
* slapgrid: Don't process not updated partitions (regression introduced in
0.28.7). [Cedric de Saint Martin]
0.28.8 (2012-09-18)
-------------------
* slapgrid: Don't process free partitions (regression introduced in 0.28.7).
[Cedric de Saint Martin]
0.28.7 (2012-09-14)
-------------------
* slapgrid: --maximal_delay reappeared to be used in special cases. [Cedric
de Saint Martin]
0.28.6 (2012-09-10)
-------------------
* register now use slapos.cfg.example from master. [Cédric Le Ninivin]
0.28.5 (2012-08-23)
-------------------
* Updated slapos.cfg for register [Cédric Le Ninivin]
0.28.4 (2012-08-22)
-------------------
* Fixed egg building.
0.28.3 (2012-08-22)
-------------------
* Avoid artificial tap creation on system check. [Łukasz Nowak]
0.28.2 (2012-08-17)
-------------------
* Resolved path problem in register [Cédric Le Ninivin]
0.28.1 (2012-08-17)
-------------------
* Resolved critical naming conflict
0.28 (2012-08-17)
-----------------
* Introduce "slapos node register" command, that will register computer to
SlapOS Master (vifib.net by default) for you. [Cédric Le Ninivin]
* Set .timestamp in partitions ONLY after slapgrid thinks it's okay (promises,
...). [Cedric de Saint Martin]
* slapgrid-ur: when destroying (not reporting), only care about instances to
destroy, completely ignore others. [Cedric de Saint Martin]
0.27 (2012-08-08)
-----------------
* slapformat: Raise correct error when no IPv6 is available on selected
interface. [Cedric de Saint Martin]
* slapgrid: Introduce --only_sr and --only_cp.
- only_sr filter and force the run of a single SR, and uses url_md5
(folder_id)
- only_cp filter which computer patition, will be runned. it can be a
list, splited by comman (slappartX,slappartY ...) [Rafael Monnerat]
* slapgrid: Cleanup unused option (--usage-report-periodicity). [Cedric de
Saint Martin]
* slapgrid: --develop will work also for Computer Partitions. [Cedric de Saint
Martin]
* slaplib: setConnectionDict won't call Master if parameters haven't changed.
[Cedric de Saint Martin]
0.26.2 (2012-07-09)
-------------------
* Define UTF-8 encoding in SlapOS Node codebase, as defined in PEP-263.
0.26.1 (2012-07-06)
-------------------
* slapgrid-sr: Add --develop option to make it ignore .completed files.
* SLAP library: it is now possible to fetch whole dict of connection
parameters.
* SLAP library: it is now possible to fetch single instance parameter.
* SLAP library: change Computer and ComputerPartition behavior to have proper
caching of computer partition parameters.
0.26 (2012-07-05)
-----------------
* slapformat: no_bridge option becomes 'not create_tap'.
create_tap is true by default. So a bridge is used and tap will be created by
default. [Cedric de Saint Martin]
* Add delay for slapformat. [Cedric Le Ninivin]
* If no software_type is given, use default one (i.e fix "error 500" when
requesting new instance). [Cedric de Saint Martin]
* slapgrid: promise based software release, new api to fetch full computer
information from server. [Yingjie Xu]
* slapproxy: new api to mock full computer information [Yingjie Xu]
* slapgrid: minor fix randomise delay feature. [Yingjie Xu]
* slapgrid: optimise slapgrid-cp, run buildout only if there is an update
on server side. [Yingjie Xu]
* libslap: Allow accessing ServerError. [Vincent Pelletier]
0.25 (2012-05-16)
-----------------
* Fix support for no_bridge option in configuration files for some values:
no_bridge = false was stated as true. [Cedric de Saint Martin]
* Delay a randomized period of time before calling slapgrid. [Yingjie Xu]
* slapformat: Don't require tunctl if no_bridge is set [Leonardo Rochael]
* slapformat: remove monkey patching when creating address so that it doesn't
return false positive. [Cedric de Saint Martin]
* Various: clearer error messages.
0.24 (2012-03-29)
-----------------
* Handles different errors in a user friendly way [Cedric de Saint Martin]
* slapgrid: Supports software destruction. [Łukasz Nowak]
* slap: added support to Supply.supply state parameter (available, destroyed)
[Łukasz Nowak]
0.23 (2012-02-29)
-----------------
* slapgrid : Don't create tarball of sofwtare release when shacache is not
configured. [Yingjie Xu]
0.22 (2012-02-09)
-----------------
* slapformat : Add no-bridge feature. [Cedric de Saint Martin]
* slapgrid : Add binary cache support. [Yingjie Xu]
0.21 (2011-12-23)
-----------------
* slap: Add renaming API. [Antoine Catton]
0.20 (2011-11-24)
-----------------
* slapgrid: Support service-less parttions. [Antoine Catton]
* slapgrid: Avoid gid collision while dropping privileges. [Antoine Catton]
* slapgrid: Drop down network usage during usage reporting. [Łukasz Nowak]
* general: Add sphinx documentation. [Romain Courteaud]
0.19 (2011-11-07)
-----------------
* bang: Executable to be called by being banged computer. [Łukasz Nowak]
0.18 (2011-10-18)
-----------------
* Fix 0.17 release: missing change for slap library. [Łukasz Nowak]
0.17 (2011-10-18)
-----------------
* slap: Avoid request under the hood. [Łukasz Nowak]
* slap: ComputerPartition.bang provided. It allows to update all instances
in tree. [Łukasz Nowak]
* slap: Computer.bang provided. It allows to bang all instances on computer.
[Łukasz Nowak]
0.16 (2011-10-03)
-----------------
* slapgrid: Bugfix for slapgrid introduced in 0.15. [Łukasz Nowak]
0.15 (2011-09-27)
-----------------
* slapgrid: Sanitize environment variables as early as possible. [Arnaud
Fontaine]
* slap: Docstring bugfix. [Sebastien Robin]
* slap: Make request asynchronous call. [Łukasz Nowak]
0.14 (2011-08-31)
-----------------
* slapgrid: Implement SSL based authentication to shadir and shacache.
[Łukasz Nowak]
* slapgrid, slap: Fix usage report packing list generation. [Nicolas Godbert]
0.13 (2011-08-25)
-----------------
* slapgrid: Implement software signing and shacache upload. [Lucas Carvalho]
* slap: Support slave instances [Gabriel Monnerat]
* slapformat: Generate always address for computer [Łukasz Nowak]
* slapgrid: Support promises scripts [Antoine Catton]
* general: slapos.core gets tests. [many contributors]
0.12 (2011-07-15)
-----------------
* Include modifications that should have been included in 0.11.
0.11 (2011-07-15)
-----------------
* Bug fix : slapconsole : shorthand methods request and supply now correctly
return an object. [Cedric de Saint Martin]
0.10 (2011-07-13)
-----------------
* Fix a bug in slapconsole where request and supply shorthand methods
don't accept all needed parameters. [Cedric de Saint Martin]
0.9 (2011-07-11)
----------------
* slapconsole: Simplify usage and use configuration file. You can now
just run slapconsole and type things like "request(kvm, 'mykvm')".
[Cedric de Saint Martin]
* slapformat: Fix issue of bridge not connected with real interface on
Linux >= 2.6.39 [Arnaud Fontaine]
* slapformat: Allow to have IPv6 only interface, with bridge still supporting
local IPv4 stack. [Łukasz Nowak]
0.8 (2011-06-27)
----------------
* slapgrid: Bugfix for temporary extends cache permissions. [Łukasz Nowak]
0.7 (2011-06-27)
----------------
* slapgrid: Fallback to buildout in own search path. [Łukasz Nowak]
0.6 (2011-06-27)
----------------
* slap: Fix bug: state shall be XML encapsulated. [Łukasz Nowak]
0.5 (2011-06-24)
----------------
* slapgrid: Use temporary extends-cache directory in order to make faster
remote profile refresh. [Łukasz Nowak]
0.4 (2011-06-24)
----------------
* general: Polish requirement versions. [Arnaud Fontaine]
* general: Remove libnetworkcache. [Lucas Carvalho]
* slap: Remove not needed method from interface. [Romain Courteaud]
* slap: state parameter is accepted and transmitted to SlapOS master [Łukasz
Nowak]
* slapformat: Implement dry run. [Vincent Pelletier]
* slapgrid: Allow to select any buildout binary used to bootstrap environment.
[Łukasz Nowak]
0.3 (2011-06-14)
----------------
* slap: Implement SLA by filter_kw in OpenOrder.request. [Łukasz Nowak]
* slap: Timeout network operations. [Łukasz Nowak]
* slapformat: Make slapsoft and slapuser* system users. [Kazuhiko Shiozaki]
* slapgrid: Add more tolerance with supervisord. [Łukasz Nowak]
0.2 (2011-06-01)
----------------
* Include required files in distribution [Łukasz Nowak]
0.1 (2011-05-27)
----------------
* Merged slapos.slap, slapos.tool.console, slapos.tool.format,
slapos.tool.grid, slapos.tool.libnetworkcache and slapos.tool.proxy into one
package: slapos.core
slapos.core-1.3.10/setup.cfg 0000644 0000000 0000000 00000000322 12517721227 014346 0 ustar root root [build_sphinx]
all_files = 1
build-dir = documentation/build
source-dir = documentation/source
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
[upload_sphinx]
upload-dir = documentation/build/html
slapos.core-1.3.10/MANIFEST.in 0000644 0000000 0000000 00000000321 12517720512 014256 0 ustar root root include CHANGES.txt
include slapos/proxy/schema.sql
include slapos/slapos-client.cfg.example
include slapos/slapos-proxy.cfg.example
include slapos/slapos.cfg.example
recursive-include slapos *.in *.txt *.xsd
slapos.core-1.3.10/setup.py 0000644 0000000 0000000 00000011000 12517720512 014226 0 ustar root root from setuptools import setup, find_packages
from shutil import copyfile
import glob
import os
from slapos.version import version
name = 'slapos.core'
long_description = open("README.txt").read() + "\n" + \
open("CHANGES.txt").read() + "\n"
for f in sorted(glob.glob(os.path.join('slapos', 'README.*.txt'))):
long_description += '\n' + open(f).read() + '\n'
slapos_folder_path = os.path.dirname(__file__)
for template_name in ('slapos-client.cfg.example',
'slapos-proxy.cfg.example', 'slapos.cfg.example'):
template_path = os.path.join(slapos_folder_path, template_name)
if os.path.exists(template_path):
copyfile(template_path,
os.path.join(slapos_folder_path, 'slapos', template_name))
additional_install_requires = []
# Even if argparse is available in python2.7, some python2.7 installations
# do not have it, so checking python version is dangerous
try:
import argparse
except ImportError:
additional_install_requires.append('argparse')
setup(name=name,
version=version,
description="SlapOS core.",
long_description=long_description,
classifiers=[
"Programming Language :: Python",
],
keywords='slapos core',
license='GPLv3',
url='http://www.slapos.org',
author='VIFIB',
namespace_packages=['slapos'],
packages=find_packages(),
include_package_data=True,
install_requires=[
'Flask', # used by proxy
'lxml', # needed to play with XML trees
'netaddr>=0.7.5', # to play safely with IPv6 prefixes
'netifaces', # to fetch information about network devices
'setuptools', # namespaces
'supervisor', # slapgrid uses supervisor to manage processes
'psutil>=2.0.0',
'xml_marshaller>=0.9.3', # to unmarshall/marshall python objects to/from
# XML
'zope.interface', # slap library implementes interfaces
'zc.buildout',
'cliff',
'requests>=2.4.3',
'uritemplate', # used by hateoas navigator
] + additional_install_requires,
extras_require={
'docs': (
'Sphinx',
'repoze.sphinx.autointerface',
'sphinxcontrib.programoutput'
),
'ipython_console': ('ipython',),
'bpython_console': ('bpython',)},
tests_require=[
'pyflakes',
'mock',
'httmock',
],
zip_safe=False, # proxy depends on Flask, which has issues with
# accessing templates
entry_points={
'console_scripts': [
'slapos-watchdog = slapos.grid.watchdog:main',
'slapos = slapos.cli.entry:main',
],
'slapos.cli': [
# Utilities
'cache lookup = slapos.cli.cache:CacheLookupCommand',
# SlapOS Node commands
'node bang = slapos.cli.bang:BangCommand',
'node format = slapos.cli.format:FormatCommand',
'node register = slapos.cli.register:RegisterCommand',
'node supervisord = slapos.cli.supervisord:SupervisordCommand',
'node supervisorctl = slapos.cli.supervisorctl:SupervisorctlCommand',
'node status = slapos.cli.supervisorctl:SupervisorctlStatusCommand',
'node start = slapos.cli.supervisorctl:SupervisorctlStartCommand',
'node stop = slapos.cli.supervisorctl:SupervisorctlStopCommand',
'node restart = slapos.cli.supervisorctl:SupervisorctlRestartCommand',
'node tail = slapos.cli.supervisorctl:SupervisorctlTailCommand',
'node report = slapos.cli.slapgrid:ReportCommand',
'node software = slapos.cli.slapgrid:SoftwareCommand',
'node instance = slapos.cli.slapgrid:InstanceCommand',
'node boot = slapos.cli.boot:BootCommand',
'node collect = slapos.cli.collect:CollectCommand',
# SlapOS client commands
'console = slapos.cli.console:ConsoleCommand',
'configure local = slapos.cli.configure_local:ConfigureLocalCommand',
'configure client = slapos.cli.configure_client:ConfigureClientCommand',
'info = slapos.cli.info:InfoCommand',
'list = slapos.cli.list:ListCommand',
'supply = slapos.cli.supply:SupplyCommand',
'remove = slapos.cli.remove:RemoveCommand',
'request = slapos.cli.request:RequestCommand',
# SlapOS Proxy commands
'proxy start = slapos.cli.proxy_start:ProxyStartCommand',
'proxy show = slapos.cli.proxy_show:ProxyShowCommand',
]
},
test_suite="slapos.tests",
)
slapos.core-1.3.10/slapos/ 0000755 0000000 0000000 00000000000 12517721227 014031 5 ustar root root slapos.core-1.3.10/slapos/bang.py 0000644 0000000 0000000 00000003770 12517720512 015315 0 ustar root root # -*- coding: utf-8 -*-
# vim: set et sts=2:
##############################################################################
#
# Copyright (c) 2011, 2012 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import slapos.slap.slap
def do_bang(configp, message):
computer_id = configp.get('slapos', 'computer_id')
master_url = configp.get('slapos', 'master_url')
if configp.has_option('slapos', 'key_file'):
key_file = configp.get('slapos', 'key_file')
else:
key_file = None
if configp.has_option('slapos', 'cert_file'):
cert_file = configp.get('slapos', 'cert_file')
else:
cert_file = None
slap = slapos.slap.slap()
slap.initializeConnection(master_url, key_file=key_file, cert_file=cert_file)
computer = slap.registerComputer(computer_id)
print 'Banging to %r' % master_url
computer.bang(message)
print 'Bang with message %r' % message
slapos.core-1.3.10/slapos/README.proxy.txt 0000644 0000000 0000000 00000000753 12517720512 016710 0 ustar root root proxy
=====
Implement minimalist SlapOS Master server without any security, designed to work
only from localhost with one SlapOS Node (a.k.a Computer).
It implements (or should implement) the SLAP API, as currently implemented in
the SlapOS Master (see slaptool.py in Master).
The only behavioral difference from the SlapOS Master is:
When the proxy doesn't find any free partition (and/or in case of slave
instance, any compatible master instance), it will throw a NotFoundError (404).
slapos.core-1.3.10/slapos/README.console.txt 0000644 0000000 0000000 00000005466 12517720512 017177 0 ustar root root console
-------
The slapconsole tool allows to interact with a SlapOS Master throught the SLAP
library.
For more information about SlapOS or slapconsole usages, please go to
http://community.slapos.org.
The slapconsole tool is only a bare Python console with several global variables
defined and initialized.
Initialization and configuration file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Slapconsole allows to automatically connect to a Master using URL and SSL
certificate from given slapos.cfg.
Certificate has to be *USER* certificate, manually obtained from SlapOS master
web interface.
Slapconsole tools reads the given slapos.cfg configuration file and use the
following informations :
* Master URL is read from [slapos] in the "master_url" parameter.
* SSL Certificate is read from [slapconsole] in the "cert_file" parameter.
* SSL Key is read from [slapconsole] in the "key_file" parameter.
See slapos.cfg.example for examples.
Global functions/variables
~~~~~~~~~~~~~~~~~~~~~~~~~~
* "request()" is a shorthand for slap.registerOpenOrder().request() allowing
to request instances.
* "supply()" is a shorthand for slap.registerSupply().supply() allowing to
request software installation.
For more information about those methods, please read the SLAP library
documentation.
* "product" is an instance of slap.SoftwareProductCollection whose only goal is to retrieve
the URL of the best Software Release of a given Software Product as attribute.
for each attribute call, it will retrieve from the SlapOS Master the best
available Software Release URL and return it.
This allows to request instances in a few words, i.e::
request("mykvm", "http://www.url.com/path/to/current/best/known/kvm/software.cfg")
can be simplified into ::
request("mykvm", product.kvm)
* "slap" is an instance of the SLAP library. It is only used for advanced usages.
"slap" instance is obtained by doing ::
slap = slapos.slap.slap()
slap.initializeConnection(config.master_url,
key_file=config.key_file, cert_file=config.cert_file)
Examples
~~~~~~~~
::
>>> # Request instance
>>> request(product.kvm, "myuniquekvm")
>>> # Request instance on specific computer
>>> request(product.kvm, "myotheruniquekvm",
filter_kw={ "computer_guid": "COMP-12345" })
>>> # Request instance, specifying parameters (here nbd_ip and nbd_port)
>>> request(product.kvm, "mythirduniquekvm",
partition_parameter_kw={"nbd_ip":"2a01:e35:2e27:460:e2cb:4eff:fed9:48dc",
"nbd_port":"1024"})
>>> # Request software installation on owned computer
>>> supply(product.kvm, "mycomputer")
>>> # Fetch existing instance status
>>> request(product.kvm, "myuniquekvm").getState()
>>> # Fetch instance information on already launched instance
>>> request(product.kvm, "myuniquekvm").getConnectionParameter("url")
slapos.core-1.3.10/slapos/client.py 0000644 0000000 0000000 00000012100 12517720512 015647 0 ustar root root # -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010, 2011, 2012 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import atexit
import ConfigParser
import os
import sys
import slapos.slap.slap
from slapos.slap import SoftwareProductCollection
SOFTWARE_PRODUCT_NAMESPACE = "product."
class ClientConfig(object):
state = None
def __init__(self, args, configp=None):
# XXX configp cannot possibly be optional
"""
Set options given by parameters.
"""
# Set options parameters
for key, value in args.__dict__.items():
setattr(self, key, value)
# Merges the arguments and configuration
try:
configuration_dict = dict(configp.items('slapconsole'))
except ConfigParser.NoSectionError:
pass
else:
for key in configuration_dict:
if not getattr(self, key, None):
setattr(self, key, configuration_dict[key])
configuration_dict = dict(configp.items('slapos'))
master_url = configuration_dict.get('master_url', None)
# Backward compatibility, if no key and certificate given in option
# take one from slapos configuration
if not getattr(self, 'key_file', None) and \
not getattr(self, 'cert_file', None):
self.key_file = configuration_dict.get('key_file')
self.cert_file = configuration_dict.get('cert_file')
if not master_url:
raise ValueError("No option 'master_url'")
elif master_url.startswith('https') and \
self.key_file is None and \
self.cert_file is None:
raise ValueError("No option 'key_file' and/or 'cert_file'")
else:
self.master_url = master_url
self.master_rest_url = configuration_dict.get('master_rest_url')
if self.key_file:
self.key_file = os.path.expanduser(self.key_file)
if self.cert_file:
self.cert_file = os.path.expanduser(self.cert_file)
def init(conf, logger):
"""Initialize Slap instance, connect to server and create
aliases to common software releases"""
# XXX check certificate and key existence
slap = slapos.slap.slap()
slap.initializeConnection(conf.master_url,
key_file=conf.key_file, cert_file=conf.cert_file,
slapgrid_rest_uri=conf.master_rest_url)
local = globals().copy()
local['slap'] = slap
# Create global shortcut functions to request instance and software
def shorthandRequest(*args, **kwargs):
return slap.registerOpenOrder().request(*args, **kwargs)
def shorthandSupply(*args, **kwargs):
# XXX-Cedric Implement computer_group support
return slap.registerSupply().supply(*args, **kwargs)
local['request'] = shorthandRequest
local['supply'] = shorthandSupply
local['product'] = SoftwareProductCollection(logger, slap)
return local
def _getSoftwareReleaseFromSoftwareString(logger, software_string, product):
"""
If Software string is a product:
Return the best Software Release URL of the Software Product "X" of the
string "product.X".
Else, return as is.
"""
if not software_string.startswith(SOFTWARE_PRODUCT_NAMESPACE):
return software_string
try:
return product.__getattr__(software_string[len(SOFTWARE_PRODUCT_NAMESPACE):])
except AttributeError as e:
logger.error('Error: %s Exiting now.' % e.message)
sys.exit(1)
def do_console(local):
# try to enable readline with completion and history
try:
import readline
except ImportError:
pass
else:
try:
import rlcompleter
readline.set_completer(rlcompleter.Completer(local).complete)
except ImportError:
pass
readline.parse_and_bind("tab: complete")
historyPath = os.path.expanduser("~/.slapconsolehistory")
def save_history(historyPath=historyPath):
readline.write_history_file(historyPath)
if os.path.exists(historyPath):
readline.read_history_file(historyPath)
atexit.register(save_history)
__import__("code").interact(banner="", local=local)
slapos.core-1.3.10/slapos/README.slap.txt 0000644 0000000 0000000 00000002714 12517720512 016465 0 ustar root root slap
====
Simple Language for Accounting and Provisioning python library.
Developer note - python version
-------------------------------
This library is used on client (slapgrid) and server side.
Server is using python2.4 and client is using python2.6
Having this in mind, code of this library *have* to work
on python2.4
How it works
------------
The SLAP main server which is in charge of service coordination receives from participating servers the number of computer paritions which are available, the type of resource which a party is ready provide, and request from parties for resources which are needed.
Each participating server is identified by a unique ID and runs a slap-server daemon. This daemon collects from the main server the installation tasks and does the installation of resources, then notifies the main server of completion whenever a resource is configured, installed and available.
The data structure on the main server is the following:
A - Action: an action which can happen to provide a resource or account its usage
CP - Computer Partition: provides a URL to Access a Cloud Resource
RI - Resource Item: describes a resource
CI - Contract Item: describes the contract to attach the DL to (This is unclear still)
R - Resource: describes a type of cloud resource (ex. MySQL Table) is published on slapgrid.org
DL - Delivery Line: Describes an action happening on a resource item on a computer partition
D - Delivery: groups multiple Delivery Lines
slapos.core-1.3.10/slapos/README.format.txt 0000644 0000000 0000000 00000001052 12517720512 017010 0 ustar root root format
======
slapformat is an application to prepare SlapOS ready node (machine).
It "formats" the machine by:
- creating users and groups
- creating bridge interface
- creating needed tap interfaces
- creating needed directories with proper ownership and permissions
In the end special report is generated and information are posted to
configured SlapOS server.
This program shall be only run by root.
Requirements
------------
Linux with IPv6, bridging and tap interface support.
Binaries:
* brctl
* groupadd
* ip
* tunctl
* useradd
slapos.core-1.3.10/slapos/util.py 0000644 0000000 0000000 00000006204 12517720512 015356 0 ustar root root # -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import errno
import os
import subprocess
def mkdir_p(path, mode=0o700):
"""\
Creates a directory and its parents, if needed.
NB: If the directory already exists, it does not change its permission.
"""
try:
os.makedirs(path, mode)
except OSError as exc:
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else:
raise
def chownDirectory(path, uid, gid):
if os.getuid() != 0:
# we are probably inside of a webrunner
return
# find /opt/slapgrid -not -user 1000 -exec chown slapsoft:slapsoft {} \;
subprocess.check_call([
'/usr/bin/find', path, '-not', '-user', str(uid), '-exec',
'/bin/chown', '%s:%s' % (uid, gid), '{}', ';'
])
def parse_certificate_key_pair(html):
"""
Extract (certificate, key) pair from an HTML page received by SlapOS Master.
"""
c_start = html.find("Certificate:")
c_end = html.find("", c_start)
certificate = html[c_start:c_end]
k_start = html.find("-----BEGIN PRIVATE KEY-----")
k_end = html.find("", k_start)
key = html[k_start:k_end]
return certificate, key
def string_to_boolean(string):
"""
Return True if the value of the "string" parameter can be parsed as True.
Return False if the value of the "string" parameter can be parsed as False.
Otherwise, Raise.
The parser is completely arbitrary, see code for actual implementation.
"""
if not isinstance(string, str) and not isinstance(string, unicode):
raise ValueError('Given value is not a string.')
acceptable_true_values = ['true']
acceptable_false_values = ['false']
string = string.lower()
if string in acceptable_true_values:
return True
if string in acceptable_false_values:
return False
else:
raise ValueError('%s is neither True nor False.' % string)
slapos.core-1.3.10/slapos/tests/ 0000755 0000000 0000000 00000000000 12517721227 015173 5 ustar root root slapos.core-1.3.10/slapos/tests/slapproxy/ 0000755 0000000 0000000 00000000000 12517721227 017234 5 ustar root root slapos.core-1.3.10/slapos/tests/slapproxy/__init__.py 0000644 0000000 0000000 00000154150 12517720512 021347 0 ustar root root # -*- coding: utf-8 -*-
# vim: set et sts=2:
##############################################################################
#
# Copyright (c) 2012, 2013, 2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import ConfigParser
import os
import logging
import shutil
import socket
import subprocess
import sys
import tempfile
import time
import unittest
import xml_marshaller
from xml_marshaller.xml_marshaller import loads, dumps
import slapos.proxy
import slapos.proxy.views as views
import slapos.slap
import slapos.slap.slap
import sqlite3
import pkg_resources
class WrongFormat(Exception):
pass
class ProxyOption(object):
"""
Will simulate options given to slapproxy
"""
def __init__(self, proxy_db):
self.verbose = True
self.database_uri = proxy_db
self.console = False
self.log_file = None
class BasicMixin(object):
def setUp(self):
"""
Will set files and start slapproxy
"""
self._tempdir = tempfile.mkdtemp()
logging.basicConfig(level=logging.DEBUG)
self.setFiles()
self.startProxy()
def createSlapOSConfigurationFile(self):
open(self.slapos_cfg, 'w').write("""[slapos]
software_root = %(tempdir)s/opt/slapgrid
instance_root = %(tempdir)s/srv/slapgrid
master_url = %(proxyaddr)s
computer_id = computer
[slapproxy]
host = 127.0.0.1
port = 8080
database_uri = %(tempdir)s/lib/proxy.db
""" % {'tempdir': self._tempdir, 'proxyaddr': self.proxyaddr})
def setFiles(self):
"""
Set environment to run slapproxy
"""
self.slapos_cfg = os.path.join(self._tempdir, 'slapos.cfg')
self.proxy_db = os.path.join(self._tempdir, 'lib', 'proxy.db')
self.proxyaddr = 'http://localhost:80/'
self.computer_id = 'computer'
self.createSlapOSConfigurationFile()
for directory in ['opt', 'srv', 'lib']:
path = os.path.join(self._tempdir, directory)
os.mkdir(path)
def startProxy(self):
"""
Set config for slapproxy and start it
"""
conf = slapos.proxy.ProxyConfig(logger=logging.getLogger())
configp = ConfigParser.SafeConfigParser()
configp.read(self.slapos_cfg)
conf.mergeConfig(ProxyOption(self.proxy_db), configp)
conf.setConfig()
views.app.config['TESTING'] = True
slapos.proxy.setupFlaskConfiguration(conf)
self.app_config = views.app.config
self.app = views.app.test_client()
def add_free_partition(self, partition_amount, computer_id=None):
"""
Will simulate a slapformat first run
and create "partition_amount" partitions
"""
if not computer_id:
computer_id = self.computer_id
computer_dict = {
'reference': computer_id,
'address': '123.456.789',
'netmask': 'fffffffff',
'partition_list': [],
}
for i in range(partition_amount):
partition_example = {
'reference': 'slappart%s' % i,
'address_list': [
{'addr': '1.2.3.4', 'netmask': '255.255.255.255'},
{'addr': '4.3.2.1', 'netmask': '255.255.255.255'}
],
'tap': {'name': 'tap0'},
}
computer_dict['partition_list'].append(partition_example)
request_dict = {
'computer_id': self.computer_id,
'xml': xml_marshaller.xml_marshaller.dumps(computer_dict),
}
rv = self.app.post('/loadComputerConfigurationFromXML',
data=request_dict)
self.assertEqual(rv._status_code, 200)
def tearDown(self):
"""
Remove files generated for test
"""
shutil.rmtree(self._tempdir, True)
views.is_schema_already_executed = False
class TestInformation(BasicMixin, unittest.TestCase):
"""
Test Basic response of slapproxy
"""
def test_getComputerInformation(self):
"""
Check that getComputerInformation return a Computer
and database is generated
"""
rv = self.app.get('/getComputerInformation?computer_id=%s' % self.computer_id)
self.assertIsInstance(
xml_marshaller.xml_marshaller.loads(rv.data),
slapos.slap.Computer)
self.assertTrue(os.path.exists(self.proxy_db))
def test_getFullComputerInformation(self):
"""
Check that getFullComputerInformation return a Computer
and database is generated
"""
rv = self.app.get('/getFullComputerInformation?computer_id=%s' % self.computer_id)
self.assertIsInstance(
xml_marshaller.xml_marshaller.loads(rv.data),
slapos.slap.Computer)
self.assertTrue(os.path.exists(self.proxy_db))
def test_getComputerInformation_wrong_computer(self):
"""
Test that computer information won't be given to a requester different
from the one specified
"""
with self.assertRaises(slapos.slap.NotFoundError):
self.app.get('/getComputerInformation?computer_id=%s42' % self.computer_id)
def test_partition_are_empty(self):
"""
Test that empty partition are empty :)
"""
self.add_free_partition(10)
rv = self.app.get('/getFullComputerInformation?computer_id=%s' % self.computer_id)
computer = xml_marshaller.xml_marshaller.loads(rv.data)
for slap_partition in computer._computer_partition_list:
self.assertIsNone(slap_partition._software_release_document)
self.assertEqual(slap_partition._requested_state, 'destroyed')
self.assertEqual(slap_partition._need_modification, 0)
def test_getSoftwareReleaseListFromSoftwareProduct_software_product_reference(self):
"""
Check that calling getSoftwareReleaseListFromSoftwareProduct() in slapproxy
using a software_product_reference as parameter behaves correctly.
"""
software_product_reference = 'my_product'
software_release_url = 'my_url'
self.app_config['software_product_list'] = {
software_product_reference: software_release_url
}
response = self.app.get('/getSoftwareReleaseListFromSoftwareProduct'
'?software_product_reference=%s' %\
software_product_reference)
software_release_url_list = xml_marshaller.xml_marshaller.loads(
response.data)
self.assertEqual(
software_release_url_list,
[software_release_url]
)
def test_getSoftwareReleaseListFromSoftwareProduct_noSoftwareProduct(self):
"""
Check that calling getSoftwareReleaseListFromSoftwareProduct() in slapproxy
using a software_product_reference that doesn't exist as parameter
returns empty list.
"""
self.app_config['software_product_list'] = {'random': 'random'}
response = self.app.get('/getSoftwareReleaseListFromSoftwareProduct'
'?software_product_reference=idonotexist')
software_release_url_list = xml_marshaller.xml_marshaller.loads(
response.data)
self.assertEqual(
software_release_url_list,
[]
)
def test_getSoftwareReleaseListFromSoftwareProduct_bothParameter(self):
"""
Test that a call to getSoftwareReleaseListFromSoftwareProduct with no
parameter raises
"""
self.assertRaises(
AssertionError,
self.app.get,
'/getSoftwareReleaseListFromSoftwareProduct'
'?software_product_reference=foo'
'&software_release_url=bar'
)
def test_getSoftwareReleaseListFromSoftwareProduct_noParameter(self):
"""
Test that a call to getSoftwareReleaseListFromSoftwareProduct with both
software_product_reference and software_release_url parameters raises
"""
self.assertRaises(
AssertionError,
self.app.get, '/getSoftwareReleaseListFromSoftwareProduct'
)
def test_getComputerPartitionCertificate(self):
"""
Tests that getComputerPartitionCertificate method is implemented in slapproxy.
"""
rv = self.app.get(
'/getComputerPartitionCertificate?computer_id=%s&computer_partition_id=%s' % (
self.computer_id, 'slappart0'))
response = xml_marshaller.xml_marshaller.loads(rv.data)
self.assertEquals({'certificate': '', 'key': ''}, response)
class MasterMixin(BasicMixin, unittest.TestCase):
"""
Define advanced tool for test proxy simulating behavior slap library tools
"""
def _requestComputerPartition(self, software_release, software_type, partition_reference,
partition_id,
shared=False, partition_parameter_kw=None, filter_kw=None,
state=None):
"""
Check parameters, call requestComputerPartition server method and return result
"""
if partition_parameter_kw is None:
partition_parameter_kw = {}
if filter_kw is None:
filter_kw = {}
# Let's enforce a default software type
if software_type is None:
software_type = 'default'
request_dict = {
'computer_id': self.computer_id,
'computer_partition_id': partition_id,
'software_release': software_release,
'software_type': software_type,
'partition_reference': partition_reference,
'shared_xml': xml_marshaller.xml_marshaller.dumps(shared),
'partition_parameter_xml': xml_marshaller.xml_marshaller.dumps(
partition_parameter_kw),
'filter_xml': xml_marshaller.xml_marshaller.dumps(filter_kw),
'state': xml_marshaller.xml_marshaller.dumps(state),
}
return self.app.post('/requestComputerPartition', data=request_dict)
def request(self, *args, **kwargs):
"""
Simulate a request with above parameters
Return response by server (a computer partition or an error)
"""
rv = self._requestComputerPartition(*args, **kwargs)
self.assertEqual(rv._status_code, 200)
xml = rv.data
software_instance = xml_marshaller.xml_marshaller.loads(xml)
computer_partition = slapos.slap.ComputerPartition(
software_instance.slap_computer_id,
software_instance.slap_computer_partition_id)
computer_partition.__dict__.update(software_instance.__dict__)
return computer_partition
def supply(self, url, computer_id=None, state=''):
if not computer_id:
computer_id = self.computer_id
request_dict = {'url':url, 'computer_id': computer_id, 'state':state}
rv = self.app.post('/supplySupply',
data=request_dict)
# XXX return a Software Release
def setConnectionDict(self, partition_id,
connection_dict, slave_reference=None):
self.app.post('/setComputerPartitionConnectionXml', data={
'computer_id': self.computer_id,
'computer_partition_id': partition_id,
'connection_xml': xml_marshaller.xml_marshaller.dumps(connection_dict),
'slave_reference': slave_reference})
def getPartitionInformation(self, computer_partition_id):
"""
Return computer information as stored in proxy for corresponding id
"""
rv = self.app.get('/getFullComputerInformation?computer_id=%s' % self.computer_id)
computer = xml_marshaller.xml_marshaller.loads(rv.data)
for instance in computer._computer_partition_list:
if instance._partition_id == computer_partition_id:
return instance
class TestRequest(MasterMixin):
"""
Set of tests for requests
"""
def test_request_consistent_parameters(self):
"""
Check that all different parameters related to requests (like instance_guid, state) are set and consistent
"""
self.add_free_partition(1)
partition = self.request('http://sr//', None, 'MyFirstInstance', 'slappart0')
self.assertEqual(partition.getState(), 'started')
self.assertEqual(partition.getInstanceGuid(), 'computer-slappart0')
def test_two_request_one_partition_free(self):
"""
Since slapproxy does not implement scope, providing two partition_id
values will still succeed, even if only one partition is available.
"""
self.add_free_partition(1)
self.assertIsInstance(self.request('http://sr//', None,
'MyFirstInstance', 'slappart2'),
slapos.slap.ComputerPartition)
self.assertIsInstance(self.request('http://sr//', None,
'MyFirstInstance', 'slappart3'),
slapos.slap.ComputerPartition)
def test_two_request_two_partition_free(self):
"""
If two requests are made with two available partition
both will succeed
"""
self.add_free_partition(2)
self.assertIsInstance(self.request('http://sr//', None,
'MyFirstInstance', 'slappart2'),
slapos.slap.ComputerPartition)
self.assertIsInstance(self.request('http://sr//', None,
'MyFirstInstance', 'slappart3'),
slapos.slap.ComputerPartition)
def test_two_same_request_from_one_partition(self):
"""
Request will return same partition for two equal requests
"""
self.add_free_partition(2)
self.assertEqual(
self.request('http://sr//', None, 'MyFirstInstance', 'slappart2').__dict__,
self.request('http://sr//', None, 'MyFirstInstance', 'slappart2').__dict__)
def test_two_requests_with_different_parameters_but_same_reference(self):
"""
Request will return same partition for two different requests but will
only update parameters
"""
self.add_free_partition(2)
wanted_domain1 = 'fou.org'
wanted_domain2 = 'carzy.org'
request1 = self.request('http://sr//', None, 'MyFirstInstance', 'slappart2',
partition_parameter_kw={'domain': wanted_domain1})
request1_dict = request1.__dict__
requested_result1 = self.getPartitionInformation(
request1_dict['_partition_id'])
request2 = self.request('http://sr1//', 'Papa', 'MyFirstInstance', 'slappart2',
partition_parameter_kw={'domain': wanted_domain2})
request2_dict = request2.__dict__
requested_result2 = self.getPartitionInformation(
request2_dict['_partition_id'])
# Test we received same partition
for key in ['_partition_id', '_computer_id']:
self.assertEqual(request1_dict[key], request2_dict[key])
# Test that only parameters changed
for key in requested_result2.__dict__:
if key not in ['_parameter_dict',
'_software_release_document']:
self.assertEqual(requested_result2.__dict__[key],
requested_result1.__dict__[key])
elif key in ['_software_release_document']:
self.assertEqual(requested_result2.__dict__[key].__dict__,
requested_result1.__dict__[key].__dict__)
#Test parameters where set correctly
self.assertEqual(wanted_domain1,
requested_result1._parameter_dict['domain'])
self.assertEqual(wanted_domain2,
requested_result2._parameter_dict['domain'])
def test_two_different_request_from_two_partition(self):
"""
Since slapproxy does not implement scope, two request with
different partition_id will still return the same partition.
"""
self.add_free_partition(2)
self.assertEqual(
self.request('http://sr//', None, 'MyFirstInstance', 'slappart2').__dict__,
self.request('http://sr//', None, 'MyFirstInstance', 'slappart3').__dict__)
def test_two_different_request_from_one_partition(self):
"""
Two different request from same partition
will return two different partitions
"""
self.add_free_partition(2)
self.assertNotEqual(
self.request('http://sr//', None, 'MyFirstInstance', 'slappart2').__dict__,
self.request('http://sr//', None, 'frontend', 'slappart2').__dict__)
class TestSlaveRequest(MasterMixin):
"""
Test requests related to slave instances.
"""
def test_slave_request_no_corresponding_partition(self):
"""
Slave instance request will fail if no corresponding are found
"""
self.add_free_partition(2)
rv = self._requestComputerPartition('http://sr//', None, 'MyFirstInstance', 'slappart2', shared=True)
self.assertEqual(rv._status_code, 404)
def test_slave_request_set_parameters(self):
"""
Parameters sent in slave request must be put in slave master
slave instance list.
1. We request a slave instance we defined parameters
2. We check parameters are in the dictionnary defining slave in
slave master slave_instance_list
"""
self.add_free_partition(6)
# Provide partition
master_partition_id = self.request('http://sr//', None,
'MyFirstInstance', 'slappart4')._partition_id
# First request of slave instance
wanted_domain = 'fou.org'
self.request('http://sr//', None, 'MyFirstInstance', 'slappart2', shared=True,
partition_parameter_kw={'domain': wanted_domain})
# Get updated information for master partition
master_partition = self.getPartitionInformation(master_partition_id)
our_slave = master_partition._parameter_dict['slave_instance_list'][0]
self.assertEqual(our_slave.get('domain'), wanted_domain)
def test_master_instance_with_no_slave(self):
"""
Test that a master instance with no requested slave
has an empty slave_instance_list parameter.
"""
self.add_free_partition(6)
# Provide partition
master_partition_id = self.request('http://sr//', None, 'MyMasterInstance', 'slappart4')._partition_id
master_partition = self.getPartitionInformation(master_partition_id)
self.assertEqual(len(master_partition._parameter_dict['slave_instance_list']), 0)
def test_slave_request_set_parameters_are_updated(self):
"""
Parameters sent in slave request must be put in slave master
slave instance list and updated when they change.
1. We request a slave instance we defined parameters
2. We check parameters are in the dictionnary defining slave in
slave master slave_instance_list
3. We request same slave instance with changed parameters
4. We check parameters are in the dictionnary defining slave in
slave master slave_instance_list have changed
"""
self.add_free_partition(6)
# Provide partition
master_partition_id = self.request('http://sr//', None,
'MyFirstInstance', 'slappart4')._partition_id
# First request of slave instance
wanted_domain_1 = 'crazy.org'
self.request('http://sr//', None, 'MyFirstInstance', 'slappart2', shared=True,
partition_parameter_kw={'domain': wanted_domain_1})
# Get updated information for master partition
master_partition = self.getPartitionInformation(master_partition_id)
our_slave = master_partition._parameter_dict['slave_instance_list'][0]
self.assertEqual(our_slave.get('domain'), wanted_domain_1)
# Second request of slave instance
wanted_domain_2 = 'maluco.org'
self.request('http://sr//', None, 'MyFirstInstance', 'slappart2', shared=True,
partition_parameter_kw={'domain': wanted_domain_2})
# Get updated information for master partition
master_partition = self.getPartitionInformation(master_partition_id)
our_slave = master_partition._parameter_dict['slave_instance_list'][0]
self.assertNotEqual(our_slave.get('domain'), wanted_domain_1)
self.assertEqual(our_slave.get('domain'), wanted_domain_2)
def test_slave_request_set_connection_parameters(self):
"""
Parameters set in slave instance by master instance must be put in slave instance connection parameters.
1. We request a slave instance
2. We set connection parameters for this slave instance
2. We check parameter is present when we do request() for the slave.
"""
self.add_free_partition(6)
# Provide partition
master_partition_id = self.request('http://sr//', None, 'MyMasterInstance', 'slappart4')._partition_id
# First request of slave instance
self.request('http://sr//', None, 'MySlaveInstance', 'slappart2', shared=True)
# Set connection parameter
master_partition = self.getPartitionInformation(master_partition_id)
# XXX change slave reference to be compatible with multiple nodes
self.setConnectionDict(partition_id=master_partition._partition_id,
connection_dict={'foo': 'bar'},
slave_reference=master_partition._parameter_dict['slave_instance_list'][0]['slave_reference'])
# Get updated information for slave partition
slave_partition = self.request('http://sr//', None, 'MySlaveInstance', 'slappart2', shared=True)
self.assertEqual(slave_partition.getConnectionParameter('foo'), 'bar')
def test_slave_request_one_corresponding_partition(self):
"""
Successfull request slave instance follow these steps:
1. Provide one corresponding partition
2. Ask for Slave instance. But no connection parameters
But slave is added to Master Instance slave list
3. Master Instance get updated information (including slave list)
4. Master instance post information about slave connection parameters
5. Ask for slave instance is successfull and return a computer instance
with connection information
"""
self.add_free_partition(6)
# Provide partition
master_partition_id = self.request('http://sr//', None,
'MyFirstInstance', 'slappart4')._partition_id
# First request of slave instance
name = 'MyFirstInstance'
requester = 'slappart2'
our_slave = self.request('http://sr//', None, name, requester, shared=True)
self.assertIsInstance(our_slave, slapos.slap.ComputerPartition)
self.assertEqual(our_slave._connection_dict, {})
# Get updated information for master partition
master_partition = self.getPartitionInformation(master_partition_id)
slave_for_master = master_partition._parameter_dict['slave_instance_list'][0]
# Send information about slave
slave_address = {'url': '%s.master.com'}
self.setConnectionDict(partition_id=master_partition._partition_id,
connection_dict=slave_address,
slave_reference=slave_for_master['slave_reference'])
# Successfull slave request with connection parameters
our_slave = self.request('http://sr//', None,
name, requester, shared=True)
self.assertIsInstance(our_slave, slapos.slap.ComputerPartition)
self.assertEqual(slave_address, our_slave._connection_dict)
def test_slave_request_instance_guid(self):
"""
Test that instance_guid support behaves correctly.
Warning: proxy doesn't gives unique id of instance, but gives instead unique id
of partition.
"""
self.add_free_partition(1)
partition = self.request('http://sr//', None, 'MyInstance', 'slappart1')
slave = self.request('http://sr//', None, 'MySlaveInstance', 'slappart1',
shared=True, filter_kw=dict(instance_guid=partition._instance_guid))
self.assertEqual(slave._partition_id, partition._partition_id)
class TestMultiNodeSupport(MasterMixin):
def test_multi_node_support_different_software_release_list(self):
"""
Test that two different registered computers have their own
Software Release list.
"""
self.add_free_partition(6, computer_id='COMP-0')
self.add_free_partition(6, computer_id='COMP-1')
software_release_1_url = 'http://sr1'
software_release_2_url = 'http://sr2'
software_release_3_url = 'http://sr3'
self.supply(software_release_1_url, 'COMP-0')
self.supply(software_release_2_url, 'COMP-1')
self.supply(software_release_3_url, 'COMP-0')
self.supply(software_release_3_url, 'COMP-1')
computer_default = loads(self.app.get('/getFullComputerInformation?computer_id=%s' % self.computer_id).data)
computer_0 = loads(self.app.get('/getFullComputerInformation?computer_id=COMP-0').data)
computer_1 = loads(self.app.get('/getFullComputerInformation?computer_id=COMP-1').data)
self.assertEqual(len(computer_default._software_release_list), 0)
self.assertEqual(len(computer_0._software_release_list), 2)
self.assertEqual(len(computer_1._software_release_list), 2)
self.assertEqual(
computer_0._software_release_list[0]._software_release,
software_release_1_url
)
self.assertEqual(
computer_0._software_release_list[0]._computer_guid,
'COMP-0'
)
self.assertEqual(
computer_0._software_release_list[1]._software_release,
software_release_3_url
)
self.assertEqual(
computer_0._software_release_list[1]._computer_guid,
'COMP-0'
)
self.assertEqual(
computer_1._software_release_list[0]._software_release,
software_release_2_url
)
self.assertEqual(
computer_1._software_release_list[0]._computer_guid,
'COMP-1'
)
self.assertEqual(
computer_1._software_release_list[1]._software_release,
software_release_3_url
)
self.assertEqual(
computer_1._software_release_list[1]._computer_guid,
'COMP-1'
)
def test_multi_node_support_remove_software_release(self):
"""
Test that removing a software from a Computer doesn't
affect other computer
"""
software_release_url = 'http://sr'
self.add_free_partition(6, computer_id='COMP-0')
self.add_free_partition(6, computer_id='COMP-1')
self.supply(software_release_url, 'COMP-0')
self.supply(software_release_url, 'COMP-1')
self.supply(software_release_url, 'COMP-0', state='destroyed')
computer_0 = loads(self.app.get('/getFullComputerInformation?computer_id=COMP-0').data)
computer_1 = loads(self.app.get('/getFullComputerInformation?computer_id=COMP-1').data)
self.assertEqual(len(computer_0._software_release_list), 0)
self.assertEqual(len(computer_1._software_release_list), 1)
self.assertEqual(
computer_1._software_release_list[0]._software_release,
software_release_url
)
self.assertEqual(
computer_1._software_release_list[0]._computer_guid,
'COMP-1'
)
def test_multi_node_support_instance_default_computer(self):
"""
Test that instance request behaves correctly with default computer
"""
software_release_url = 'http://sr'
computer_0_id = 'COMP-0'
computer_1_id = 'COMP-1'
self.add_free_partition(6, computer_id=computer_0_id)
self.add_free_partition(6, computer_id=computer_1_id)
# Request without SLA -> goes to default computer only.
# It should fail if we didn't registered partitions for default computer
# (default computer is always registered)
rv = self._requestComputerPartition('http://sr//', None, 'MyFirstInstance', 'slappart2')
self.assertEqual(rv._status_code, 404)
rv = self._requestComputerPartition('http://sr//', None, 'MyFirstInstance', 'slappart2',
filter_kw={'computer_guid':self.computer_id})
self.assertEqual(rv._status_code, 404)
# Register default computer: deployment works
self.add_free_partition(1)
self.request('http://sr//', None, 'MyFirstInstance', 'slappart0')
computer_default = loads(self.app.get(
'/getFullComputerInformation?computer_id=%s' % self.computer_id).data)
self.assertEqual(len(computer_default._software_release_list), 0)
# No free space on default computer: request without SLA fails
rv = self._requestComputerPartition('http://sr//', None, 'CanIHasPartition', 'slappart2',
filter_kw={'computer_guid':self.computer_id})
self.assertEqual(rv._status_code, 404)
def test_multi_node_support_instance(self):
"""
Test that instance request behaves correctly with several
registered computers
"""
software_release_url = 'http://sr'
computer_0_id = 'COMP-0'
computer_1_id = 'COMP-1'
software_release_1 = 'http://sr//'
software_release_2 = 'http://othersr//'
self.add_free_partition(2, computer_id=computer_1_id)
# Deploy to first non-default computer using SLA
# It should fail since computer is not registered
rv = self._requestComputerPartition(software_release_1, None, 'MyFirstInstance', 'slappart2', filter_kw={'computer_guid':computer_0_id})
self.assertEqual(rv._status_code, 404)
self.add_free_partition(2, computer_id=computer_0_id)
# Deploy to first non-default computer using SLA
partition = self.request(software_release_1, None, 'MyFirstInstance', 'slappart0', filter_kw={'computer_guid':computer_0_id})
self.assertEqual(partition.getState(), 'started')
self.assertEqual(partition._partition_id, 'slappart0')
self.assertEqual(partition._computer_id, computer_0_id)
# All other instances should be empty
computer_0 = loads(self.app.get('/getFullComputerInformation?computer_id=COMP-0').data)
computer_1 = loads(self.app.get('/getFullComputerInformation?computer_id=COMP-1').data)
self.assertEqual(computer_0._computer_partition_list[0]._software_release_document._software_release, software_release_1)
self.assertTrue(computer_0._computer_partition_list[1]._software_release_document == None)
self.assertTrue(computer_1._computer_partition_list[0]._software_release_document == None)
self.assertTrue(computer_1._computer_partition_list[1]._software_release_document == None)
# Deploy to second non-default computer using SLA
partition = self.request(software_release_2, None, 'MySecondInstance', 'slappart0', filter_kw={'computer_guid':computer_1_id})
self.assertEqual(partition.getState(), 'started')
self.assertEqual(partition._partition_id, 'slappart0')
self.assertEqual(partition._computer_id, computer_1_id)
# The two remaining instances should be free, and MyfirstInstance should still be there
computer_0 = loads(self.app.get('/getFullComputerInformation?computer_id=COMP-0').data)
computer_1 = loads(self.app.get('/getFullComputerInformation?computer_id=COMP-1').data)
self.assertEqual(computer_0._computer_partition_list[0]._software_release_document._software_release, software_release_1)
self.assertTrue(computer_0._computer_partition_list[1]._software_release_document == None)
self.assertEqual(computer_1._computer_partition_list[0]._software_release_document._software_release, software_release_2)
self.assertTrue(computer_1._computer_partition_list[1]._software_release_document == None)
def test_multi_node_support_change_instance_state(self):
"""
Test that destroying an instance (i.e change state) from a Computer doesn't
affect other computer
"""
software_release_url = 'http://sr'
computer_0_id = 'COMP-0'
computer_1_id = 'COMP-1'
self.add_free_partition(6, computer_id=computer_0_id)
self.add_free_partition(6, computer_id=computer_1_id)
partition_first = self.request('http://sr//', None, 'MyFirstInstance', 'slappart0', filter_kw={'computer_guid':computer_0_id})
partition_second = self.request('http://sr//', None, 'MySecondInstance', 'slappart0', filter_kw={'computer_guid':computer_1_id})
partition_first = self.request('http://sr//', None, 'MyFirstInstance', 'slappart0', filter_kw={'computer_guid':computer_0_id}, state='stopped')
computer_0 = loads(self.app.get('/getFullComputerInformation?computer_id=COMP-0').data)
computer_1 = loads(self.app.get('/getFullComputerInformation?computer_id=COMP-1').data)
self.assertEqual(computer_0._computer_partition_list[0].getState(), 'stopped')
self.assertEqual(computer_0._computer_partition_list[1].getState(), 'destroyed')
self.assertEqual(computer_1._computer_partition_list[0].getState(), 'started')
self.assertEqual(computer_1._computer_partition_list[1].getState(), 'destroyed')
def test_multi_node_support_same_reference(self):
"""
Test that requesting an instance with same reference to two
different nodes behaves like master: once an instance is assigned to a node,
changing SLA will not change node.
"""
software_release_url = 'http://sr'
computer_0_id = 'COMP-0'
computer_1_id = 'COMP-1'
self.add_free_partition(2, computer_id=computer_0_id)
self.add_free_partition(2, computer_id=computer_1_id)
partition = self.request('http://sr//', None, 'MyFirstInstance', 'slappart0', filter_kw={'computer_guid':computer_0_id})
partition = self.request('http://sr//', None, 'MyFirstInstance', 'slappart0', filter_kw={'computer_guid':computer_1_id})
self.assertEqual(partition._computer_id, computer_0_id)
computer_1 = loads(self.app.get('/getFullComputerInformation?computer_id=COMP-1').data)
self.assertTrue(computer_1._computer_partition_list[0]._software_release_document == None)
self.assertTrue(computer_1._computer_partition_list[1]._software_release_document == None)
def test_multi_node_support_slave_instance(self):
"""
Test that slave instances are correctly deployed if SLA is specified
but deployed only on default computer if not specified (i.e not deployed
if default computer doesn't have corresponding master instance).
"""
computer_0_id = 'COMP-0'
computer_1_id = 'COMP-1'
self.add_free_partition(2, computer_id=computer_0_id)
self.add_free_partition(2, computer_id=computer_1_id)
self.add_free_partition(2)
self.request('http://sr2//', None, 'MyFirstInstance', 'slappart0', filter_kw={'computer_guid':computer_0_id})
self.request('http://sr//', None, 'MyOtherInstance', 'slappart0', filter_kw={'computer_guid':computer_1_id})
# Request slave without SLA: will fail
rv = self._requestComputerPartition('http://sr//', None, 'MySlaveInstance', 'slappart2', shared=True)
self.assertEqual(rv._status_code, 404)
# Request slave with SLA on incorrect computer: will fail
rv = self._requestComputerPartition('http://sr//', None, 'MySlaveInstance', 'slappart2', shared=True, filter_kw={'computer_guid':computer_0_id})
self.assertEqual(rv._status_code, 404)
# Request computer on correct computer: will succeed
partition = self.request('http://sr//', None, 'MySlaveInstance', 'slappart2', shared=True, filter_kw={'computer_guid':computer_1_id})
self.assertEqual(partition._computer_id, computer_1_id)
def test_multi_node_support_instance_guid(self):
"""
Test that instance_guid support behaves correctly with multiple nodes.
Warning: proxy doesn't gives unique id of instance, but gives instead unique id
of partition.
"""
computer_0_id = 'COMP-0'
computer_1_id = 'COMP-1'
self.add_free_partition(2, computer_id=computer_0_id)
self.add_free_partition(2, computer_id=computer_1_id)
self.add_free_partition(2)
partition_computer_0 = self.request('http://sr2//', None, 'MyFirstInstance', 'slappart0', filter_kw={'computer_guid':computer_0_id})
partition_computer_1 = self.request('http://sr//', None, 'MyOtherInstance', 'slappart0', filter_kw={'computer_guid':computer_1_id})
partition_computer_default = self.request('http://sr//', None, 'MyThirdInstance', 'slappart0')
self.assertEqual(partition_computer_0.getInstanceGuid(), 'COMP-0-slappart0')
self.assertEqual(partition_computer_1.getInstanceGuid(), 'COMP-1-slappart0')
self.assertEqual(partition_computer_default.getInstanceGuid(), 'computer-slappart0')
def test_multi_node_support_getComputerInformation(self):
"""
Test that computer information will not be given if computer is not registered.
Test that it still should work for the 'default' computer specified in slapos config
even if not yet registered.
Test that computer information is given if computer is registered.
"""
new_computer_id = '%s42' % self.computer_id
with self.assertRaises(slapos.slap.NotFoundError):
self.app.get('/getComputerInformation?computer_id=%s42' % new_computer_id)
try:
self.app.get('/getComputerInformation?computer_id=%s' % self.computer_id)
except slapos.slap.NotFoundError:
self.fail('Could not fetch informations for default computer.')
self.add_free_partition(1, computer_id=new_computer_id)
try:
self.app.get('/getComputerInformation?computer_id=%s' % new_computer_id)
except slapos.slap.NotFoundError:
self.fail('Could not fetch informations for registered computer.')
class TestMultiMasterSupport(MasterMixin):
"""
Test multimaster support in slapproxy.
"""
external_software_release = 'http://mywebsite.me/exteral_software_release.cfg'
software_release_not_in_list = 'http://mywebsite.me/exteral_software_release_not_listed.cfg'
def setUp(self):
self.addCleanup(self.stopExternalProxy)
# XXX don't use lo
self.external_proxy_host = os.environ.get('LOCAL_IPV4', '127.0.0.1')
self.external_proxy_port = 8281
self.external_master_url = 'http://%s:%s' % (self.external_proxy_host, self.external_proxy_port)
self.external_computer_id = 'external_computer'
self.external_proxy_slap = slapos.slap.slap()
self.external_proxy_slap.initializeConnection(self.external_master_url)
super(TestMultiMasterSupport, self).setUp()
self.db = sqlite3.connect(self.proxy_db)
self.external_slapproxy_configuration_file_location = os.path.join(
self._tempdir, 'external_slapos.cfg')
self.createExternalProxyConfigurationFile()
self.startExternalProxy()
def tearDown(self):
super(TestMultiMasterSupport, self).tearDown()
def createExternalProxyConfigurationFile(self):
open(self.external_slapproxy_configuration_file_location, 'w').write("""[slapos]
computer_id = %(external_computer_id)s
[slapproxy]
host = %(host)s
port = %(port)s
database_uri = %(tempdir)s/lib/external_proxy.db
""" % {
'tempdir': self._tempdir,
'host': self.external_proxy_host,
'port': self.external_proxy_port,
'external_computer_id': self.external_computer_id
})
def startExternalProxy(self):
"""
Start external slapproxy
"""
logging.getLogger().info('Starting external proxy, listening to %s:%s' % (self.external_proxy_host, self.external_proxy_port))
# XXX This uses a hack to run current code of slapos.core
import slapos
self.external_proxy_process = subprocess.Popen(
[
sys.executable, '%s/cli/entry.py' % os.path.dirname(slapos.__file__),
'proxy', 'start', '--cfg', self.external_slapproxy_configuration_file_location
],
env={"PYTHONPATH": ':'.join(sys.path)}
)
# Wait a bit for proxy to be started
attempts = 0
while (attempts < 20):
try:
self.external_proxy_slap._connection_helper.GET('/')
except slapos.slap.NotFoundError:
break
except slapos.slap.ConnectionError, socket.error:
attempts = attempts + 1
time.sleep(0.1)
else:
self.fail('Could not start external proxy.')
def stopExternalProxy(self):
self.external_proxy_process.kill()
def createSlapOSConfigurationFile(self):
"""
Overwrite default slapos configuration file to enable specific multimaster
behaviours.
"""
configuration = pkg_resources.resource_stream(
'slapos.tests.slapproxy', 'slapos_multimaster.cfg.in'
).read() % {
'tempdir': self._tempdir, 'proxyaddr': self.proxyaddr,
'external_proxy_host': self.external_proxy_host,
'external_proxy_port': self.external_proxy_port
}
open(self.slapos_cfg, 'w').write(configuration)
def external_proxy_add_free_partition(self, partition_amount, computer_id=None):
"""
Will simulate a slapformat first run
and create "partition_amount" partitions
"""
if not computer_id:
computer_id = self.external_computer_id
computer_dict = {
'reference': computer_id,
'address': '123.456.789',
'netmask': 'fffffffff',
'partition_list': [],
}
for i in range(partition_amount):
partition_example = {
'reference': 'slappart%s' % i,
'address_list': [
{'addr': '1.2.3.4', 'netmask': '255.255.255.255'},
{'addr': '4.3.2.1', 'netmask': '255.255.255.255'}
],
'tap': {'name': 'tap0'},
}
computer_dict['partition_list'].append(partition_example)
request_dict = {
'computer_id': self.computer_id,
'xml': xml_marshaller.xml_marshaller.dumps(computer_dict),
}
self.external_proxy_slap._connection_helper.POST('/loadComputerConfigurationFromXML',
data=request_dict)
def _checkInstanceIsFowarded(self, name, partition_parameter_kw, software_release):
"""
Test there is no instance on local proxy.
Test there is instance on external proxy.
Test there is instance reference in external table of databse of local proxy.
"""
# Test it has been correctly added to local database
forwarded_instance_list = slapos.proxy.views.execute_db('forwarded_partition_request', 'SELECT * from %s', db=self.db)
self.assertEqual(len(forwarded_instance_list), 1)
forwarded_instance = forwarded_instance_list[0]
self.assertEqual(forwarded_instance['partition_reference'], name)
self.assertEqual(forwarded_instance['master_url'], self.external_master_url)
# Test there is nothing allocated locally
computer = loads(self.app.get(
'/getFullComputerInformation?computer_id=%s' % self.computer_id
).data)
self.assertEqual(
computer._computer_partition_list[0]._software_release_document,
None
)
# Test there is an instance allocated in external master
external_slap = slapos.slap.slap()
external_slap.initializeConnection(self.external_master_url)
external_computer = external_slap.registerComputer(self.external_computer_id)
external_partition = external_computer.getComputerPartitionList()[0]
for k, v in partition_parameter_kw.iteritems():
self.assertEqual(
external_partition.getInstanceParameter(k),
v
)
self.assertEqual(
external_partition._software_release_document._software_release,
software_release
)
def _checkInstanceIsAllocatedLocally(self, name, partition_parameter_kw, software_release):
"""
Test there is one instance on local proxy.
Test there NO is instance reference in external table of databse of local proxy.
Test there is not instance on external proxy.
"""
# Test it has NOT been added to local database
forwarded_instance_list = slapos.proxy.views.execute_db('forwarded_partition_request', 'SELECT * from %s', db=self.db)
self.assertEqual(len(forwarded_instance_list), 0)
# Test there is an instance allocated locally
computer = loads(self.app.get(
'/getFullComputerInformation?computer_id=%s' % self.computer_id
).data)
partition = computer._computer_partition_list[0]
for k, v in partition_parameter_kw.iteritems():
self.assertEqual(
partition.getInstanceParameter(k),
v
)
self.assertEqual(
partition._software_release_document._software_release,
software_release
)
# Test there is NOT instance allocated in external master
external_slap = slapos.slap.slap()
external_slap.initializeConnection(self.external_master_url)
external_computer = external_slap.registerComputer(self.external_computer_id)
external_partition = external_computer.getComputerPartitionList()[0]
self.assertEqual(
external_partition._software_release_document,
None
)
def testForwardToMasterInList(self):
"""
Test that explicitely asking a master_url in SLA causes
proxy to forward request to this master.
"""
dummy_parameter_dict = {'foo': 'bar'}
instance_reference = 'MyFirstInstance'
self.add_free_partition(1)
self.external_proxy_add_free_partition(1)
filter_kw = {'master_url': self.external_master_url}
partition = self.request(self.software_release_not_in_list, None, instance_reference, 'slappart0',
filter_kw=filter_kw, partition_parameter_kw=dummy_parameter_dict)
self._checkInstanceIsFowarded(instance_reference, dummy_parameter_dict, self.software_release_not_in_list)
self.assertEqual(
partition._master_url,
self.external_master_url
)
def testForwardToMasterNotInList(self):
"""
Test that explicitely asking a master_url in SLA causes
proxy to refuse to forward if this master_url is not whitelisted
"""
self.add_free_partition(1)
self.external_proxy_add_free_partition(1)
filter_kw = {'master_url': self.external_master_url + 'bad'}
rv = self._requestComputerPartition(self.software_release_not_in_list, None, 'MyFirstInstance', 'slappart0', filter_kw=filter_kw)
self.assertEqual(rv._status_code, 404)
def testForwardRequest_SoftwareReleaseList(self):
"""
Test that instance request is automatically forwarded
if its Software Release matches list.
"""
dummy_parameter_dict = {'foo': 'bar'}
instance_reference = 'MyFirstInstance'
self.add_free_partition(1)
self.external_proxy_add_free_partition(1)
partition = self.request(self.external_software_release, None, instance_reference, 'slappart0',
partition_parameter_kw=dummy_parameter_dict)
self._checkInstanceIsFowarded(instance_reference, dummy_parameter_dict, self.external_software_release)
def testRequestToCurrentMaster(self):
"""
Explicitely ask deployment of an instance to current master
"""
self.add_free_partition(1)
self.external_proxy_add_free_partition(1)
instance_reference = 'MyFirstInstance'
dummy_parameter_dict = {'foo': 'bar'}
filter_kw = {'master_url': self.proxyaddr}
self.request(self.software_release_not_in_list, None, instance_reference, 'slappart0',
filter_kw=filter_kw, partition_parameter_kw=dummy_parameter_dict)
self._checkInstanceIsAllocatedLocally(instance_reference, dummy_parameter_dict, self.software_release_not_in_list)
def testRequestExplicitelyOnExternalMasterThenRequestAgain(self):
"""
Request an instance that will get forwarded to another an instance.
Test that subsequent request without SLA doesn't forward
"""
dummy_parameter_dict = {'foo': 'bar'}
self.testForwardToMasterInList()
partition = self.request(self.software_release_not_in_list, None, 'MyFirstInstance', 'slappart0', partition_parameter_kw=dummy_parameter_dict)
self.assertEqual(
getattr(partition, '_master_url', None),
None
)
# Test it has not been removed from local database (we keep track)
forwarded_instance_list = slapos.proxy.views.execute_db('forwarded_partition_request', 'SELECT * from %s', db=self.db)
self.assertEqual(len(forwarded_instance_list), 1)
# Test there is an instance allocated locally
computer = loads(self.app.get(
'/getFullComputerInformation?computer_id=%s' % self.computer_id
).data)
partition = computer._computer_partition_list[0]
for k, v in dummy_parameter_dict.iteritems():
self.assertEqual(
partition.getInstanceParameter(k),
v
)
self.assertEqual(
partition._software_release_document._software_release,
self.software_release_not_in_list
)
# XXX: when testing new schema version,
# rename to "TestMigrateVersion10ToLatest" and test accordingly.
# Of course, also test version 11 to latest (should be 12).
class TestMigrateVersion10To11(TestInformation, TestRequest, TestSlaveRequest, TestMultiNodeSupport):
"""
Test that old database version are automatically migrated without failure
"""
def setUp(self):
super(TestMigrateVersion10To11, self).setUp()
schema = pkg_resources.resource_stream('slapos.tests.slapproxy', 'database_dump_version_10.sql')
schema = schema.read() % dict(version='11')
self.db = sqlite3.connect(self.proxy_db)
self.db.cursor().executescript(schema)
self.db.commit()
def test_automatic_migration(self):
table_list = ('software11', 'computer11', 'partition11', 'slave11', 'partition_network11')
for table in table_list:
self.assertRaises(sqlite3.OperationalError, self.db.execute, "SELECT name FROM computer11")
# Run a dummy request to cause migration
self.app.get('/getComputerInformation?computer_id=computer')
# Check some partition parameters
self.assertEqual(
loads(self.app.get('/getComputerInformation?computer_id=computer').data)._computer_partition_list[0]._parameter_dict['slap_software_type'],
'production'
)
# Lower level tests
computer_list = self.db.execute("SELECT * FROM computer11").fetchall()
self.assertEqual(
computer_list,
[(u'computer', u'127.0.0.1', u'255.255.255.255')]
)
software_list = self.db.execute("SELECT * FROM software11").fetchall()
self.assertEqual(
software_list,
[(u'/srv/slapgrid//srv//runner/project//slapos/software.cfg', u'computer')]
)
partition_list = self.db.execute("select * from partition11").fetchall()
self.assertEqual(
partition_list,
[(u'slappart0', u'computer', u'busy', u'/srv/slapgrid//srv//runner/project//slapos/software.cfg', u'\n\n {\n "site-id": "erp5"\n }\n}\n\n', None, None, u'production', u'slapos', None, u'started'), (u'slappart1', u'computer', u'busy', u'/srv/slapgrid//srv//runner/project//slapos/software.cfg', u"\n\n", u'\n\n mysql://127.0.0.1:45678/erp5\n\n', None, u'mariadb', u'MariaDB DataBase', u'slappart0', u'started'), (u'slappart2', u'computer', u'busy', u'/srv/slapgrid//srv//runner/project//slapos/software.cfg', u'\n\n \n\n', u'\n\n cloudooo://127.0.0.1:23000/\n\n', None, u'cloudooo', u'Cloudooo', u'slappart0', u'started'), (u'slappart3', u'computer', u'busy', u'/srv/slapgrid//srv//runner/project//slapos/software.cfg', u"\n\n", u'\n\n memcached://127.0.0.1:11000/\n\n', None, u'memcached', u'Memcached', u'slappart0', u'started'), (u'slappart4', u'computer', u'busy', u'/srv/slapgrid//srv//runner/project//slapos/software.cfg', u"\n\n", u'\n\n memcached://127.0.0.1:13301/\n\n', None, u'kumofs', u'KumoFS', u'slappart0', u'started'), (u'slappart5', u'computer', u'busy', u'/srv/slapgrid//srv//runner/project//slapos/software.cfg', u'\n\n memcached://127.0.0.1:13301/\n memcached://127.0.0.1:11000/\n cloudooo://127.0.0.1:23000/\n\n', u'\n\n https://[fc00::1]:10001\n\n', None, u'tidstorage', u'TidStorage', u'slappart0', u'started'), (u'slappart6', u'computer', u'free', None, None, None, None, None, None, None, u'started'), (u'slappart7', u'computer', u'free', None, None, None, None, None, None, None, u'started'), (u'slappart8', u'computer', u'free', None, None, None, None, None, None, None, u'started'), (u'slappart9', u'computer', u'free', None, None, None, None, None, None, None, u'started')]
)
slave_list = self.db.execute("select * from slave11").fetchall()
self.assertEqual(
slave_list,
[]
)
partition_network_list = self.db.execute("select * from partition_network11").fetchall()
self.assertEqual(
partition_network_list,
[(u'slappart0', u'computer', u'slappart0', u'127.0.0.1', u'255.255.255.255'), (u'slappart0', u'computer', u'slappart0', u'fc00::1', u'ffff:ffff:ffff::'), (u'slappart1', u'computer', u'slappart1', u'127.0.0.1', u'255.255.255.255'), (u'slappart1', u'computer', u'slappart1', u'fc00::1', u'ffff:ffff:ffff::'), (u'slappart2', u'computer', u'slappart2', u'127.0.0.1', u'255.255.255.255'), (u'slappart2', u'computer', u'slappart2', u'fc00::1', u'ffff:ffff:ffff::'), (u'slappart3', u'computer', u'slappart3', u'127.0.0.1', u'255.255.255.255'), (u'slappart3', u'computer', u'slappart3', u'fc00::1', u'ffff:ffff:ffff::'), (u'slappart4', u'computer', u'slappart4', u'127.0.0.1', u'255.255.255.255'), (u'slappart4', u'computer', u'slappart4', u'fc00::1', u'ffff:ffff:ffff::'), (u'slappart5', u'computer', u'slappart5', u'127.0.0.1', u'255.255.255.255'), (u'slappart5', u'computer', u'slappart5', u'fc00::1', u'ffff:ffff:ffff::'), (u'slappart6', u'computer', u'slappart6', u'127.0.0.1', u'255.255.255.255'), (u'slappart6', u'computer', u'slappart6', u'fc00::1', u'ffff:ffff:ffff::'), (u'slappart7', u'computer', u'slappart7', u'127.0.0.1', u'255.255.255.255'), (u'slappart7', u'computer', u'slappart7', u'fc00::1', u'ffff:ffff:ffff::'), (u'slappart8', u'computer', u'slappart8', u'127.0.0.1', u'255.255.255.255'), (u'slappart8', u'computer', u'slappart8', u'fc00::1', u'ffff:ffff:ffff::'), (u'slappart9', u'computer', u'slappart9', u'127.0.0.1', u'255.255.255.255'), (u'slappart9', u'computer', u'slappart9', u'fc00::1', u'ffff:ffff:ffff::')]
)
# Override several tests that needs an empty database
@unittest.skip("Not implemented")
def test_multi_node_support_different_software_release_list(self):
pass
@unittest.skip("Not implemented")
def test_multi_node_support_instance_default_computer(self):
pass
@unittest.skip("Not implemented")
def test_multi_node_support_instance_guid(self):
pass
@unittest.skip("Not implemented")
def test_partition_are_empty(self):
pass
@unittest.skip("Not implemented")
def test_request_consistent_parameters(self):
pass
slapos.core-1.3.10/slapos/tests/slapproxy/slapos_multimaster.cfg.in 0000644 0000000 0000000 00000002247 12517720512 024252 0 ustar root root [slapos]
software_root = %(tempdir)s/opt/slapgrid
instance_root = %(tempdir)s/srv/slapgrid
master_url = %(proxyaddr)s
computer_id = computer
[slapproxy]
host = 127.0.0.1
port = 8080
database_uri = %(tempdir)s/lib/proxy.db
# Here goes the list of slapos masters that slapproxy can contact
# Each section beginning by multimaster is a different SlapOS Master, represented by arbitrary name.
# For each section, you need to specify the URL of the SlapOS Master.
# For each section, you can specify if needed the location of key/certificate used to authenticate to this slapOS Master.
# For each section, you can specify a list of Software Releases. Any instance request matching this Softwrare Release will be automatically forwarded to this SlapOS Master and will not be allocated locally.
[multimaster/https://slap.vifib.com]
key = /path/to/cert.key
cert = /path/to/cert.cert
# XXX add wildcard support for SR list.
software_release_list =
http://something.io/software.cfg
/some/arbitrary/local/unix/path
[multimaster/http://%(external_proxy_host)s:%(external_proxy_port)s]
# No certificate here: it is http.
software_release_list =
http://mywebsite.me/exteral_software_release.cfg
slapos.core-1.3.10/slapos/tests/client.py 0000644 0000000 0000000 00000007430 12517720512 017023 0 ustar root root ##############################################################################
#
# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import logging
import unittest
import slapos.slap
import slapos.client
class TestClient(unittest.TestCase):
def setUp(self):
self.called_software_product = None
class FakeSoftwareProductCollection(object):
def __init__(inner_self, *args, **kw_args):
inner_self.__getattr__ = inner_self.get
def get(inner_self, software_product):
self.called_software_product = software_product
return self.software_product_reference
self.slap = slapos.slap.slap()
self.product_collection = FakeSoftwareProductCollection(
logging.getLogger(), self.slap)
def test_getSoftwareReleaseFromSoftwareString_softwareProduct(self):
"""
Test that if given software is a Sofwtare Product (i.e matching
the magic string), it returns the corresponding value of a call to
SoftwareProductCollection.
"""
self.software_product_reference = 'foo'
software_string = '%s%s' % (
slapos.client.SOFTWARE_PRODUCT_NAMESPACE,
self.software_product_reference
)
slapos.client._getSoftwareReleaseFromSoftwareString(
logging.getLogger(), software_string, self.product_collection)
self.assertEqual(
self.called_software_product,
self.software_product_reference
)
def test_getSoftwareReleaseFromSoftwareString_softwareProduct_emptySoftwareProduct(self):
"""
Test that if given software is a Software Product (i.e matching
the magic string), but this software product is empty, it exits.
"""
self.software_product_reference = 'foo'
software_string = '%s%s' % (
slapos.client.SOFTWARE_PRODUCT_NAMESPACE,
self.software_product_reference
)
def fake_get(software_product):
raise AttributeError()
self.product_collection.__getattr__ = fake_get
self.assertRaises(
SystemExit,
slapos.client._getSoftwareReleaseFromSoftwareString,
logging.getLogger(), software_string, self.product_collection
)
def test_getSoftwareReleaseFromSoftwareString_softwareRelease(self):
"""
Test that if given software is a simple Software Release URL (not matching
the magic string), it is just returned without modification.
"""
software_string = 'foo'
returned_value = slapos.client._getSoftwareReleaseFromSoftwareString(
logging.getLogger(), software_string, self.product_collection)
self.assertEqual(
self.called_software_product,
None
)
self.assertEqual(
returned_value,
software_string
)
slapos.core-1.3.10/slapos/tests/collect.py 0000644 0000000 0000000 00000050035 12517720512 017171 0 ustar root root ##############################################################################
#
# Copyright (c) 2014 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import os
import glob
import unittest
import shutil
import tarfile
import tempfile
import slapos.slap
import psutil
from time import strftime
from slapos.collect import entity, snapshot, db, reporter
from slapos.cli.entry import SlapOSApp
from ConfigParser import ConfigParser
class FakeDatabase(object):
def __init__(self):
self.invoked_method_list = []
def connect(self):
self.invoked_method_list.append(("connect", ""))
def close(self):
self.invoked_method_list.append(("close", ""))
def commit(self):
self.invoked_method_list.append(("commit", ""))
def insertUserSnapshot(self, *args, **kw):
self.invoked_method_list.append(("insertUserSnapshot", (args, kw)))
def insertSystemSnapshot(self, *args, **kw):
self.invoked_method_list.append(("insertSystemSnapshot", (args, kw)))
def insertComputerSnapshot(self, *args, **kw):
self.invoked_method_list.append(("insertComputerSnapshot", (args, kw)))
def insertDiskPartitionSnapshot(self, *args, **kw):
self.invoked_method_list.append(("insertDiskPartitionSnapshot", (args, kw)))
class TestCollectDatabase(unittest.TestCase):
def setUp(self):
self.instance_root = tempfile.mkdtemp()
def tearDown(self):
if os.path.exists(self.instance_root):
shutil.rmtree(self.instance_root)
def test_database_bootstrap(self):
self.assertFalse(os.path.exists(
"%s/collector.db" % self.instance_root ))
database = db.Database(self.instance_root)
database.connect()
try:
self.assertEquals(
[u'user', u'computer', u'system', u'disk'],
database.getTableList())
finally:
database.close()
self.assertTrue(os.path.exists(
"%s/collector.db" % self.instance_root ))
def test_insert_user_snapshot(self):
database = db.Database(self.instance_root)
database.connect()
try:
database.insertUserSnapshot(
'fakeuser0', 10, '10-12345', '0.1', '10.0', '1',
'10.0', '10.0', '0.1', '0.1', 'DATE', 'TIME')
database.commit()
self.assertEquals([i for i in database.select('user')],
[(u'fakeuser0', 10.0, u'10-12345', 0.1, 10.0,
1.0, 10.0, 10.0, 0.1, 0.1, u'DATE', u'TIME', 0)])
finally:
database.close()
def test_insert_computer_snapshot(self):
database = db.Database(self.instance_root)
database.connect()
try:
database.insertComputerSnapshot(
'1', '0', '0', '100', '0', '/dev/sdx1', 'DATE', 'TIME')
database.commit()
self.assertEquals([i for i in database.select('computer')],
[(1.0, 0.0, u'0', 100.0, u'0', u'/dev/sdx1', u'DATE', u'TIME', 0)])
finally:
database.close()
def test_insert_disk_partition_snapshot(self):
database = db.Database(self.instance_root)
database.connect()
try:
database.insertDiskPartitionSnapshot(
'/dev/sdx1', '10', '20', '/mnt', 'DATE', 'TIME')
database.commit()
self.assertEquals([i for i in database.select('disk')],
[(u'/dev/sdx1', u'10', u'20', u'/mnt', u'DATE', u'TIME', 0)])
finally:
database.close()
def test_insert_system_snapshot(self):
database = db.Database(self.instance_root)
database.connect()
try:
database.insertSystemSnapshot("0.1", '10.0', '100.0', '100.0',
'10.0', '1', '2', '12.0', '1', '1', 'DATE', 'TIME')
database.commit()
self.assertEquals([i for i in database.select('system')],
[(0.1, 10.0, 100.0, 100.0, 10.0, 1.0,
2.0, 12.0, 1.0, 1.0, u'DATE', u'TIME', 0)])
finally:
database.close()
def test_date_scope(self):
database = db.Database(self.instance_root)
database.connect()
try:
database.insertSystemSnapshot("0.1", '10.0', '100.0', '100.0',
'10.0', '1', '2', '12.0', '1', '1', 'EXPECTED-DATE', 'TIME')
database.commit()
self.assertEquals([i for i in database.getDateScopeList()],
[('EXPECTED-DATE', 1)])
self.assertEquals([i for i in \
database.getDateScopeList(ignore_date='EXPECTED-DATE')],
[])
self.assertEquals([i for i in \
database.getDateScopeList(reported=1)],
[])
finally:
database.close()
def test_garbage_collection_date_list(self):
database = db.Database(self.instance_root)
self.assertEquals(len(database._getGarbageCollectionDateList()), 3)
self.assertEquals(len(database._getGarbageCollectionDateList(1)), 1)
self.assertEquals(len(database._getGarbageCollectionDateList(0)), 0)
self.assertEquals(database._getGarbageCollectionDateList(1),
[strftime("%Y-%m-%d")])
def test_garbage(self):
database = db.Database(self.instance_root)
database.connect()
database.insertSystemSnapshot("0.1", '10.0', '100.0', '100.0',
'10.0', '1', '2', '12.0', '1', '1', '1983-01-10', 'TIME')
database.insertDiskPartitionSnapshot(
'/dev/sdx1', '10', '20', '/mnt', '1983-01-10', 'TIME')
database.insertComputerSnapshot(
'1', '0', '0', '100', '0', '/dev/sdx1', '1983-01-10', 'TIME')
database.commit()
database.markDayAsReported(date_scope="1983-01-10",
table_list=database.table_list)
database.commit()
self.assertEquals(len([x for x in database.select('system')]), 1)
self.assertEquals(len([x for x in database.select('computer')]), 1)
self.assertEquals(len([x for x in database.select('disk')]), 1)
database.close()
database.garbageCollect()
database.connect()
self.assertEquals(len([x for x in database.select('system')]), 0)
self.assertEquals(len([x for x in database.select('computer')]), 0)
self.assertEquals(len([x for x in database.select('disk')]), 0)
def test_mark_day_as_reported(self):
database = db.Database(self.instance_root)
database.connect()
try:
database.insertSystemSnapshot("0.1", '10.0', '100.0', '100.0',
'10.0', '1', '2', '12.0', '1', '1', 'EXPECTED-DATE', 'TIME')
database.insertSystemSnapshot("0.1", '10.0', '100.0', '100.0',
'10.0', '1', '2', '12.0', '1', '1', 'NOT-EXPECTED-DATE', 'TIME')
database.commit()
self.assertEquals([i for i in database.select('system')],
[(0.1, 10.0, 100.0, 100.0, 10.0, 1.0,
2.0, 12.0, 1.0, 1.0, u'EXPECTED-DATE', u'TIME', 0),
(0.1, 10.0, 100.0, 100.0, 10.0, 1.0,
2.0, 12.0, 1.0, 1.0, u'NOT-EXPECTED-DATE', u'TIME', 0)])
database.markDayAsReported(date_scope="EXPECTED-DATE",
table_list=["system"])
database.commit()
self.assertEquals([i for i in database.select('system')],
[(0.1, 10.0, 100.0, 100.0, 10.0, 1.0,
2.0, 12.0, 1.0, 1.0, u'EXPECTED-DATE', u'TIME', 1),
(0.1, 10.0, 100.0, 100.0, 10.0, 1.0,
2.0, 12.0, 1.0, 1.0, u'NOT-EXPECTED-DATE', u'TIME', 0)])
finally:
database.close()
class TestCollectReport(unittest.TestCase):
def setUp(self):
self.instance_root = tempfile.mkdtemp()
def tearDown(self):
if os.path.exists(self.instance_root):
shutil.rmtree(self.instance_root)
def test_raw_csv_report(self):
database = db.Database(self.instance_root)
database.connect()
database.insertSystemSnapshot("0.1", '10.0', '100.0', '100.0',
'10.0', '1', '2', '12.0', '1', '1', '1983-01-10', 'TIME')
database.insertDiskPartitionSnapshot(
'/dev/sdx1', '10', '20', '/mnt', '1983-01-10', 'TIME')
database.insertComputerSnapshot(
'1', '0', '0', '100', '0', '/dev/sdx1', '1983-01-10', 'TIME')
database.commit()
database.close()
reporter.RawCSVDumper(database).dump(self.instance_root)
self.assertTrue(os.path.exists("%s/1983-01-10" % self.instance_root))
csv_path_list = ['%s/1983-01-10/dump_disk.csv' % self.instance_root,
'%s/1983-01-10/dump_computer.csv' % self.instance_root,
'%s/1983-01-10/dump_user.csv' % self.instance_root,
'%s/1983-01-10/dump_system.csv' % self.instance_root]
self.assertEquals(set(glob.glob("%s/1983-01-10/*.csv" % self.instance_root)),
set(csv_path_list))
def test_system_csv_report(self):
database = db.Database(self.instance_root)
database.connect()
database.insertSystemSnapshot("0.1", '10.0', '100.0', '100.0',
'10.0', '1', '2', '12.0', '1', '1', strftime("%Y-%m-%d"), 'TIME')
database.insertDiskPartitionSnapshot(
'/dev/sdx1', '10', '20', '/mnt', strftime("%Y-%m-%d"), 'TIME')
database.insertComputerSnapshot(
'1', '0', '0', '100', '0', '/dev/sdx1', strftime("%Y-%m-%d"), 'TIME')
database.commit()
database.close()
reporter.SystemCSVReporterDumper(database).dump(self.instance_root)
csv_path_list = ['%s/system_memory_used.csv' % self.instance_root,
'%s/system_cpu_percent.csv' % self.instance_root,
'%s/system_net_out_bytes.csv' % self.instance_root,
'%s/system_net_in_bytes.csv' % self.instance_root,
'%s/system_disk_memory_free__dev_sdx1.csv' % self.instance_root,
'%s/system_net_out_errors.csv' % self.instance_root,
'%s/system_disk_memory_used__dev_sdx1.csv' % self.instance_root,
'%s/system_net_out_dropped.csv' % self.instance_root,
'%s/system_memory_free.csv' % self.instance_root,
'%s/system_net_in_errors.csv' % self.instance_root,
'%s/system_net_in_dropped.csv' % self.instance_root,
'%s/system_loadavg.csv' % self.instance_root]
self.assertEquals(set(glob.glob("%s/*.csv" % self.instance_root)), set(csv_path_list))
def test_compress_log_directory(self):
log_directory = "%s/test_compress" % self.instance_root
dump_folder = "%s/1990-01-01" % log_directory
if not os.path.exists(log_directory):
os.mkdir(log_directory)
if os.path.exists(dump_folder):
shutil.rmtree(dump_folder)
os.mkdir("%s/1990-01-01" % log_directory)
with open("%s/test.txt" % dump_folder, "w") as dump_file:
dump_file.write("hi")
dump_file.close()
reporter.compressLogFolder(log_directory)
self.assertFalse(os.path.exists(dump_folder))
self.assertTrue(os.path.exists("%s.tar.gz" % dump_folder))
with tarfile.open("%s.tar.gz" % dump_folder) as tf:
self.assertEquals(tf.getmembers()[0].name, "1990-01-01")
self.assertEquals(tf.getmembers()[1].name, "1990-01-01/test.txt")
self.assertEquals(tf.extractfile(tf.getmembers()[1]).read(), 'hi')
class TestCollectSnapshot(unittest.TestCase):
def setUp(self):
self.slap = slapos.slap.slap()
self.app = SlapOSApp()
self.temp_dir = tempfile.mkdtemp()
os.environ["HOME"] = self.temp_dir
self.instance_root = tempfile.mkdtemp()
self.software_root = tempfile.mkdtemp()
if os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
def test_process_snapshot(self):
process = psutil.Process(os.getpid())
process_snapshot = snapshot.ProcessSnapshot(process)
self.assertNotEquals(process_snapshot.username, None)
self.assertEquals(int(process_snapshot.pid), os.getpid())
self.assertEquals(int(process_snapshot.name.split("-")[0]),
os.getpid())
self.assertNotEquals(process_snapshot.cpu_percent , None)
self.assertNotEquals(process_snapshot.cpu_time , None)
self.assertNotEquals(process_snapshot.cpu_num_threads, None)
self.assertNotEquals(process_snapshot.memory_percent , None)
self.assertNotEquals(process_snapshot.memory_rss, None)
self.assertNotEquals(process_snapshot.io_rw_counter, None)
self.assertNotEquals(process_snapshot.io_cycles_counter, None)
def test_process_snapshot_broken_process(self):
self.assertRaises(AssertionError,
snapshot.ProcessSnapshot, None)
def test_computer_snapshot(self):
computer_snapshot = snapshot.ComputerSnapshot()
self.assertNotEquals(computer_snapshot.cpu_num_core , None)
self.assertNotEquals(computer_snapshot.cpu_frequency , None)
self.assertNotEquals(computer_snapshot.cpu_type , None)
self.assertNotEquals(computer_snapshot.memory_size , None)
self.assertNotEquals(computer_snapshot.memory_type , None)
self.assertEquals(type(computer_snapshot.system_snapshot),
snapshot.SystemSnapshot)
self.assertNotEquals(computer_snapshot.disk_snapshot_list, [])
self.assertNotEquals(computer_snapshot.partition_list, [])
self.assertEquals(type(computer_snapshot.disk_snapshot_list[0]),
snapshot.DiskPartitionSnapshot)
def test_system_snapshot(self):
system_snapshot = snapshot.SystemSnapshot()
self.assertNotEquals(system_snapshot.memory_used , None)
self.assertNotEquals(system_snapshot.memory_free , None)
self.assertNotEquals(system_snapshot.memory_percent , None)
self.assertNotEquals(system_snapshot.cpu_percent , None)
self.assertNotEquals(system_snapshot.load , None)
self.assertNotEquals(system_snapshot.net_in_bytes , None)
self.assertNotEquals(system_snapshot.net_in_errors, None)
self.assertNotEquals(system_snapshot.net_in_dropped , None)
self.assertNotEquals(system_snapshot.net_out_bytes , None)
self.assertNotEquals(system_snapshot.net_out_errors, None)
self.assertNotEquals(system_snapshot.net_out_dropped , None)
class TestCollectEntity(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.mkdtemp()
os.environ["HOME"] = self.temp_dir
self.instance_root = tempfile.mkdtemp()
self.software_root = tempfile.mkdtemp()
if os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
def tearDown(self):
pass
def getFakeUser(self):
return entity.User("fakeuser0",
"%s/fakeuser0" % self.instance_root )
def test_get_user_list(self):
config = ConfigParser()
config.add_section('slapformat')
config.set('slapformat', 'partition_amount', '3')
config.set('slapformat', 'user_base_name', 'slapuser')
config.set('slapformat', 'partition_base_name', 'slappart')
config.add_section('slapos')
config.set('slapos', 'instance_root', self.instance_root)
user_dict = entity.get_user_list(config)
username_list = ['slapuser0', 'slapuser1', 'slapuser2']
self.assertEquals(username_list, user_dict.keys())
for name in username_list:
self.assertEquals(user_dict[name].name, name)
self.assertEquals(user_dict[name].snapshot_list, [])
expected_path = "%s/slappart%s" % (self.instance_root, name.strip("slapuser"))
self.assertEquals(user_dict[name].path, expected_path)
def test_user_add_snapshot(self):
user = self.getFakeUser()
self.assertEquals(user.snapshot_list, [])
user.append("SNAPSHOT")
self.assertEquals(user.snapshot_list, ["SNAPSHOT"])
def test_user_save(self):
user = self.getFakeUser()
process = psutil.Process(os.getpid())
user.append(snapshot.ProcessSnapshot(process))
database = FakeDatabase()
user.save(database, "DATE", "TIME")
self.assertEquals(database.invoked_method_list[0], ("connect", ""))
self.assertEquals(database.invoked_method_list[1][0], "insertUserSnapshot")
self.assertEquals(database.invoked_method_list[1][1][0], ("fakeuser0",))
self.assertEquals(database.invoked_method_list[1][1][1].keys(),
['cpu_time', 'cpu_percent', 'process',
'memory_rss', 'pid', 'memory_percent',
'io_rw_counter', 'insertion_date', 'insertion_time',
'io_cycles_counter', 'cpu_num_threads'])
self.assertEquals(database.invoked_method_list[2], ("commit", ""))
self.assertEquals(database.invoked_method_list[3], ("close", ""))
def test_computer_entity(self):
computer = entity.Computer(snapshot.ComputerSnapshot())
database = FakeDatabase()
computer.save(database, "DATE", "TIME")
self.assertEquals(database.invoked_method_list[0], ("connect", ""))
self.assertEquals(database.invoked_method_list[1][0], "insertComputerSnapshot")
self.assertEquals(database.invoked_method_list[1][1][0], ())
self.assertEquals(database.invoked_method_list[1][1][1].keys(),
['insertion_time', 'insertion_date', 'cpu_num_core',
'partition_list', 'cpu_frequency', 'memory_size',
'cpu_type', 'memory_type'])
self.assertEquals(database.invoked_method_list[2][0], "insertSystemSnapshot")
self.assertEquals(database.invoked_method_list[2][1][0], ())
self.assertEquals(set(database.invoked_method_list[2][1][1].keys()),
set([ 'memory_used', 'cpu_percent', 'insertion_date', 'insertion_time',
'loadavg', 'memory_free', 'net_in_bytes', 'net_in_dropped',
'net_in_errors', 'net_out_bytes', 'net_out_dropped',
'net_out_errors']))
self.assertEquals(database.invoked_method_list[3][0], "insertDiskPartitionSnapshot")
self.assertEquals(database.invoked_method_list[3][1][0], ())
self.assertEquals(set(database.invoked_method_list[3][1][1].keys()),
set([ 'used', 'insertion_date', 'partition', 'free',
'mountpoint', 'insertion_time' ]))
self.assertEquals(database.invoked_method_list[-2], ("commit", ""))
self.assertEquals(database.invoked_method_list[-1], ("close", ""))
slapos.core-1.3.10/slapos/tests/util.py 0000644 0000000 0000000 00000012204 12517720512 016515 0 ustar root root ##############################################################################
#
# Copyright (c) 2013 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import os
import slapos.util
from slapos.util import string_to_boolean
import tempfile
import unittest
import shutil
from pwd import getpwnam
class TestUtil(unittest.TestCase):
"""
Tests methods available in the slapos.util module.
"""
def test_mkdir_p_new_directory(self):
"""
Test that mkdir_p recursively creates a directory.
"""
root_directory = tempfile.mkdtemp()
wanted_directory = os.path.join(root_directory, 'foo', 'bar')
slapos.util.mkdir_p(wanted_directory)
self.assertTrue(os.path.isdir(wanted_directory))
shutil.rmtree(root_directory)
def test_mkdir_already_existing(self):
"""
Check that mkdir_p doesn't raise if directory already exist.
"""
root_directory = tempfile.mkdtemp()
slapos.util.mkdir_p(root_directory)
self.assertTrue(os.path.isdir(root_directory))
shutil.rmtree(root_directory)
def test_chown_directory(self):
"""
Test that slapos.util.chownDirectory correctly changes owner.
Note: requires root privileges.
"""
root_slaptest = tempfile.mkdtemp()
wanted_directory0 = os.path.join(root_slaptest, 'slap-write0')
wanted_directory1 = os.path.join(root_slaptest, 'slap-write0', 'write-slap1')
wanted_directory2 = os.path.join(root_slaptest, 'slap-write0', 'write-slap1', 'write-teste2')
wanted_directory_mkdir0 = os.makedirs(wanted_directory0, mode=0777)
wanted_directory_mkdir1 = os.makedirs(wanted_directory1, mode=0777)
wanted_directory_mkdir2 = os.makedirs(wanted_directory2, mode=0777)
create_file_txt = tempfile.mkstemp(suffix='.txt', prefix='tmp', dir=wanted_directory2, text=True)
user = 'nobody'
try:
uid = getpwnam(user)[2]
gid = getpwnam(user)[3]
except KeyError:
raise unittest.SkipTest("user %s doesn't exist." % user)
if os.getuid() != 0:
raise unittest.SkipTest("No root privileges, impossible to chown.")
slapos.util.chownDirectory(root_slaptest, uid, gid)
uid_check_root_slaptest = os.stat(root_slaptest)[4]
gid_check_root_slaptest = os.stat(root_slaptest)[5]
self.assertEquals(uid, uid_check_root_slaptest)
self.assertEquals(gid, gid_check_root_slaptest)
uid_check_wanted_directory0 = os.stat(wanted_directory0)[4]
gid_check_wanted_directory0 = os.stat(wanted_directory0)[5]
self.assertEquals(uid, uid_check_wanted_directory0)
self.assertEquals(gid, gid_check_wanted_directory0)
uid_check_wanted_directory1 = os.stat(wanted_directory1)[4]
gid_check_wanted_directory1 = os.stat(wanted_directory1)[5]
self.assertEquals(uid, uid_check_wanted_directory1)
self.assertEquals(gid, gid_check_wanted_directory1)
uid_check_wanted_directory2 = os.stat(wanted_directory2)[4]
gid_check_wanted_directory2 = os.stat(wanted_directory2)[5]
self.assertEquals(uid, uid_check_wanted_directory2)
self.assertEquals(gid, gid_check_wanted_directory2)
uid_check_file_txt = os.stat(create_file_txt[1])[4]
gid_check_file_txt = os.stat(create_file_txt[1])[5]
self.assertEquals(uid, uid_check_file_txt)
self.assertEquals(gid, gid_check_file_txt)
shutil.rmtree(root_slaptest)
def test_string_to_boolean_with_true_values(self):
"""
Check that mkdir_p doesn't raise if directory already exist.
"""
for value in ['true', 'True', 'TRUE']:
self.assertTrue(string_to_boolean(value))
def test_string_to_boolean_with_false_values(self):
"""
Check that mkdir_p doesn't raise if directory already exist.
"""
for value in ['false', 'False', 'False']:
self.assertFalse(string_to_boolean(value))
def test_string_to_boolean_with_incorrect_values(self):
"""
Check that mkdir_p doesn't raise if directory already exist.
"""
for value in [True, False, 1, '1', 't', 'tru', 'truelle', 'f', 'fals', 'falsey']:
self.assertRaises(ValueError, string_to_boolean, value)
if __name__ == '__main__':
unittest.main()
slapos.core-1.3.10/slapos/tests/slapobject.py 0000644 0000000 0000000 00000041610 12517720512 017671 0 ustar root root ##############################################################################
#
# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import logging
import os
import time
import unittest
from slapos.slap import ComputerPartition as SlapComputerPartition
from slapos.grid.SlapObject import Partition, Software
from slapos.grid import utils
from slapos.grid import networkcache
# XXX: BasicMixin should be in a separated module, not in slapgrid test module.
from slapos.tests.slapgrid import BasicMixin
# Mockup
# XXX: Ambiguous name
# XXX: Factor with common SlapOS tests
class FakeCallAndStore(object):
"""
Used to check if the mocked method has been called.
"""
def __init__(self):
self.called = False
def __call__(self, *args, **kwargs):
self.called = True
class FakeCallAndNoop(object):
"""
Used to no-op a method.
"""
def __call__(self, *args, **kwargs):
pass
# XXX: change name and behavior to be more generic and factor with other tests
class FakeNetworkCacheCallAndRead(object):
"""
Short-circuit normal calls to slapos buildout helpers, get and store
'additional_buildout_parameter_list' for future analysis.
"""
def __init__(self):
self.external_command_list = []
def __call__(self, *args, **kwargs):
additional_buildout_parameter_list = \
kwargs.get('additional_buildout_parameter_list')
self.external_command_list.extend(additional_buildout_parameter_list)
# Backup modules
original_install_from_buildout = Software._install_from_buildout
original_upload_network_cached = networkcache.upload_network_cached
originalBootstrapBuildout = utils.bootstrapBuildout
originalLaunchBuildout = utils.launchBuildout
originalUploadSoftwareRelease = Software.uploadSoftwareRelease
originalPartitionGenerateSupervisorConfigurationFile = Partition.generateSupervisorConfigurationFile
class MasterMixin(BasicMixin, unittest.TestCase):
"""
Master Mixin of slapobject test classes.
"""
def setUp(self):
BasicMixin.setUp(self)
os.mkdir(self.software_root)
os.mkdir(self.instance_root)
def tearDown(self):
BasicMixin.tearDown(self)
# Un-monkey patch possible modules
global originalBootstrapBuildout
global originalLaunchBuildout
utils.bootstrapBuildout = originalBootstrapBuildout
utils.launchBuildout = originalLaunchBuildout
# Helper functions
def createSoftware(self, url=None, empty=False):
"""
Create an empty software, and return a Software object from
dummy parameters.
"""
if url is None:
url = 'mysoftware'
software_path = os.path.join(self.software_root, utils.md5digest(url))
os.mkdir(software_path)
if not empty:
# Populate the Software Release directory so that it is "complete" and
# "working" from a slapos point of view.
open(os.path.join(software_path, 'instance.cfg'), 'w').close()
return Software(
url=url,
software_root=self.software_root,
buildout=self.buildout,
logger=logging.getLogger(),
)
def createPartition(
self,
software_release_url,
partition_id=None,
slap_computer_partition=None,
retention_delay=None,
):
"""
Create a partition, and return a Partition object created
from dummy parameters.
"""
# XXX dirty, should disappear when Partition is cleaned up
software_path = os.path.join(
self.software_root,
utils.md5digest(software_release_url)
)
if partition_id is None:
partition_id = 'mypartition'
if slap_computer_partition is None:
slap_computer_partition = SlapComputerPartition(
computer_id='bidon',
partition_id=partition_id)
instance_path = os.path.join(self.instance_root, partition_id)
os.mkdir(instance_path)
os.chmod(instance_path, 0o750)
supervisor_configuration_path = os.path.join(
self.instance_root, 'supervisor')
os.mkdir(supervisor_configuration_path)
partition = Partition(
software_path=software_path,
instance_path=instance_path,
supervisord_partition_configuration_path=os.path.join(
supervisor_configuration_path, partition_id),
supervisord_socket=os.path.join(
supervisor_configuration_path, 'supervisor.sock'),
computer_partition=slap_computer_partition,
computer_id='bidon',
partition_id=partition_id,
server_url='bidon',
software_release_url=software_release_url,
buildout=self.buildout,
logger=logging.getLogger(),
)
partition.updateSupervisor = FakeCallAndNoop
if retention_delay:
partition.retention_delay = retention_delay
return partition
class TestSoftwareNetworkCacheSlapObject(MasterMixin, unittest.TestCase):
"""
Test for Network Cache related features in Software class.
"""
def setUp(self):
MasterMixin.setUp(self)
self.fakeCallAndRead = FakeNetworkCacheCallAndRead()
utils.bootstrapBuildout = self.fakeCallAndRead
utils.launchBuildout = self.fakeCallAndRead
self.signature_private_key_file = '/signature/private/key_file'
self.upload_cache_url = 'http://example.com/uploadcache'
self.upload_dir_url = 'http://example.com/uploaddir'
self.shacache_cert_file = '/path/to/shacache/cert/file'
self.shacache_key_file = '/path/to/shacache/key/file'
self.shadir_cert_file = '/path/to/shadir/cert/file'
self.shadir_key_file = '/path/to/shadir/key/file'
def tearDown(self):
MasterMixin.tearDown(self)
Software._install_from_buildout = original_install_from_buildout
networkcache.upload_network_cached = original_upload_network_cached
Software.uploadSoftwareRelease = originalUploadSoftwareRelease
# Test methods
def test_software_install_with_networkcache(self):
"""
Check if the networkcache parameters are propagated.
"""
software = Software(
url='http://example.com/software.cfg',
software_root=self.software_root,
buildout=self.buildout,
logger=logging.getLogger(),
signature_private_key_file='/signature/private/key_file',
upload_cache_url='http://example.com/uploadcache',
upload_dir_url='http://example.com/uploaddir',
shacache_cert_file=self.shacache_cert_file,
shacache_key_file=self.shacache_key_file,
shadir_cert_file=self.shadir_cert_file,
shadir_key_file=self.shadir_key_file)
software.install()
command_list = self.fakeCallAndRead.external_command_list
self.assertIn('buildout:networkcache-section=networkcache', command_list)
self.assertIn('networkcache:signature-private-key-file=%s' % self.signature_private_key_file, command_list)
self.assertIn('networkcache:upload-cache-url=%s' % self.upload_cache_url, command_list)
self.assertIn('networkcache:upload-dir-url=%s' % self.upload_dir_url, command_list)
self.assertIn('networkcache:shacache-cert-file=%s' % self.shacache_cert_file, command_list)
self.assertIn('networkcache:shacache-key-file=%s' % self.shacache_key_file, command_list)
self.assertIn('networkcache:shadir-cert-file=%s' % self.shadir_cert_file, command_list)
self.assertIn('networkcache:shadir-key-file=%s' % self.shadir_key_file, command_list)
def test_software_install_without_networkcache(self):
"""
Check if the networkcache parameters are not propagated if they are not
available.
"""
software = Software(url='http://example.com/software.cfg',
software_root=self.software_root,
buildout=self.buildout,
logger=logging.getLogger())
software.install()
command_list = self.fakeCallAndRead.external_command_list
self.assertNotIn('buildout:networkcache-section=networkcache', command_list)
self.assertNotIn('networkcache:signature-private-key-file=%s' %
self.signature_private_key_file,
command_list)
self.assertNotIn('networkcache:upload-cache-url=%s' % self.upload_cache_url,
command_list)
self.assertNotIn('networkcache:upload-dir-url=%s' % self.upload_dir_url,
command_list)
# XXX-Cedric: do the same with upload
def test_software_install_networkcache_upload_blacklist(self):
"""
Check if the networkcache upload blacklist parameters are propagated.
"""
def fakeBuildout(*args, **kw):
pass
Software._install_from_buildout = fakeBuildout
def fake_upload_network_cached(*args, **kw):
self.assertFalse(True)
networkcache.upload_network_cached = fake_upload_network_cached
upload_to_binary_cache_url_blacklist = ["http://example.com"]
software = Software(
url='http://example.com/software.cfg',
software_root=self.software_root,
buildout=self.buildout,
logger=logging.getLogger(),
signature_private_key_file='/signature/private/key_file',
upload_cache_url='http://example.com/uploadcache',
upload_dir_url='http://example.com/uploaddir',
shacache_cert_file=self.shacache_cert_file,
shacache_key_file=self.shacache_key_file,
shadir_cert_file=self.shadir_cert_file,
shadir_key_file=self.shadir_key_file,
upload_to_binary_cache_url_blacklist=
upload_to_binary_cache_url_blacklist,
)
software.install()
def test_software_install_networkcache_upload_blacklist_side_effect(self):
"""
Check if the networkcache upload blacklist parameters only prevent
blacklisted Software Release to be uploaded.
"""
def fakeBuildout(*args, **kw):
pass
Software._install_from_buildout = fakeBuildout
def fakeUploadSoftwareRelease(*args, **kw):
self.uploaded = True
Software.uploadSoftwareRelease = fakeUploadSoftwareRelease
upload_to_binary_cache_url_blacklist = ["http://anotherexample.com"]
software = Software(
url='http://example.com/software.cfg',
software_root=self.software_root,
buildout=self.buildout,
logger=logging.getLogger(),
signature_private_key_file='/signature/private/key_file',
upload_cache_url='http://example.com/uploadcache',
upload_dir_url='http://example.com/uploaddir',
upload_binary_cache_url='http://example.com/uploadcache',
upload_binary_dir_url='http://example.com/uploaddir',
shacache_cert_file=self.shacache_cert_file,
shacache_key_file=self.shacache_key_file,
shadir_cert_file=self.shadir_cert_file,
shadir_key_file=self.shadir_key_file,
upload_to_binary_cache_url_blacklist=
upload_to_binary_cache_url_blacklist,
)
software.install()
self.assertTrue(getattr(self, 'uploaded', False))
class TestPartitionSlapObject(MasterMixin, unittest.TestCase):
def setUp(self):
MasterMixin.setUp(self)
Partition.generateSupervisorConfigurationFile = FakeCallAndNoop()
utils.bootstrapBuildout = FakeCallAndNoop()
utils.launchBuildout = FakeCallAndStore()
def tearDown(self):
MasterMixin.tearDown(self)
Partition.generateSupervisorConfigurationFile = originalPartitionGenerateSupervisorConfigurationFile
def test_instance_is_deploying_if_software_release_exists(self):
"""
Test that slapgrid deploys an instance if its Software Release exists and
instance.cfg in the Software Release exists.
"""
software = self.createSoftware()
partition = self.createPartition(software.url)
partition.install()
self.assertTrue(utils.launchBuildout.called)
def test_backward_compatibility_instance_is_deploying_if_template_cfg_is_used(self):
"""
Backward compatibility test, for old software releases.
Test that slapgrid deploys an instance if its Software Release exists and
template.cfg in the Software Release exists.
"""
software = self.createSoftware(empty=True)
open(os.path.join(software.software_path, 'template.cfg'), 'w').close()
partition = self.createPartition(software.url)
partition.install()
self.assertTrue(utils.launchBuildout.called)
def test_instance_slapgrid_raise_if_software_release_instance_profile_does_not_exist(self):
"""
Test that slapgrid raises XXX when deploying an instance if the Software Release
related to the instance is not correctly installed (i.e there is no
instance.cfg in it).
"""
software = self.createSoftware(empty=True)
partition = self.createPartition(software.url)
# XXX: What should it raise?
self.assertRaises(IOError, partition.install)
def test_instance_slapgrid_raise_if_software_release_does_not_exist(self):
"""
Test that slapgrid raises XXX when deploying an instance if the Software Release
related to the instance is not present at all (i.e its directory does not
exist at all).
"""
software = self.createSoftware(empty=True)
os.rmdir(software.software_path)
partition = self.createPartition(software.url)
# XXX: What should it raise?
self.assertRaises(IOError, partition.install)
class TestPartitionDestructionLock(MasterMixin, unittest.TestCase):
def setUp(self):
MasterMixin.setUp(self)
Partition.generateSupervisorConfigurationFile = FakeCallAndNoop()
utils.bootstrapBuildout = FakeCallAndNoop()
utils.launchBuildout = FakeCallAndStore()
def test_retention_lock_delay_creation(self):
delay = 42
software = self.createSoftware()
partition = self.createPartition(software.url, retention_delay=delay)
partition.install()
deployed_delay = int(open(partition.retention_lock_delay_file_path).read())
self.assertEqual(delay, deployed_delay)
def test_no_retention_lock_delay(self):
software = self.createSoftware()
partition = self.createPartition(software.url)
partition.install()
delay = open(partition.retention_lock_delay_file_path).read()
self.assertTrue(delay, '0')
self.assertTrue(partition.destroy())
def test_retention_lock_delay_does_not_change(self):
delay = 42
software = self.createSoftware()
partition = self.createPartition(software.url, retention_delay=delay)
partition.install()
partition.retention_delay = 23
# install/destroy many times
partition.install()
partition.destroy()
partition.destroy()
partition.install()
partition.destroy()
deployed_delay = int(open(partition.retention_lock_delay_file_path).read())
self.assertEqual(delay, deployed_delay)
def test_retention_lock_delay_is_respected(self):
delay = 2.0 / (3600 * 24)
software = self.createSoftware()
partition = self.createPartition(software.url, retention_delay=delay)
partition.install()
deployed_delay = float(open(partition.retention_lock_delay_file_path).read())
self.assertEqual(int(delay), int(deployed_delay))
self.assertFalse(partition.destroy())
time.sleep(1)
self.assertFalse(partition.destroy())
time.sleep(1)
self.assertTrue(partition.destroy())
def test_retention_lock_date_creation(self):
delay = 42
software = self.createSoftware()
partition = self.createPartition(software.url, retention_delay=delay)
partition.install()
self.assertFalse(os.path.exists(partition.retention_lock_date_file_path))
partition.destroy()
deployed_date = float(open(partition.retention_lock_date_file_path).read())
self.assertEqual(delay * 3600 * 24 + int(time.time()), int(deployed_date))
def test_retention_lock_date_does_not_change(self):
delay = 42
software = self.createSoftware()
partition = self.createPartition(software.url, retention_delay=delay)
now = time.time()
partition.install()
partition.destroy()
partition.retention_delay = 23
# install/destroy many times
partition.install()
partition.destroy()
partition.destroy()
partition.install()
partition.destroy()
deployed_date = float(open(partition.retention_lock_date_file_path).read())
self.assertEqual(delay * 3600 * 24 + int(now), int(deployed_date))
slapos.core-1.3.10/slapos/tests/distribution.py 0000644 0000000 0000000 00000006536 12517720512 020272 0 ustar root root # -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import unittest
from slapos.grid import distribution
class TestDebianize(unittest.TestCase):
def test_debian_major(self):
"""
On debian, we only care about major release.
All the other tuples are unchanged.
"""
for provided, expected in [
(('CentOS', '6.3', 'Final'), None),
(('Ubuntu', '12.04', 'precise'), None),
(('Ubuntu', '13.04', 'raring'), None),
(('Fedora', '17', 'Beefy Miracle'), None),
(('debian', '6.0.6', ''), ('debian', '6', '')),
(('debian', '7.0', ''), ('debian', '7', '')),
]:
self.assertEqual(distribution._debianize(provided), expected or provided)
class TestOSMatches(unittest.TestCase):
def test_centos(self):
self.assertFalse(distribution.os_matches(('CentOS', '6.3', 'Final'),
('Ubuntu', '13.04', 'raring')))
self.assertFalse(distribution.os_matches(('CentOS', '6.3', 'Final'),
('debian', '6.3', '')))
def test_ubuntu(self):
self.assertFalse(distribution.os_matches(('Ubuntu', '12.04', 'precise'),
('Ubuntu', '13.04', 'raring')))
self.assertTrue(distribution.os_matches(('Ubuntu', '13.04', 'raring'),
('Ubuntu', '13.04', 'raring')))
self.assertTrue(distribution.os_matches(('Ubuntu', '12.04', 'precise'),
('Ubuntu', '12.04', 'precise')))
def test_debian(self):
self.assertFalse(distribution.os_matches(('debian', '6.0.6', ''),
('debian', '7.0', '')))
self.assertTrue(distribution.os_matches(('debian', '6.0.6', ''),
('debian', '6.0.5', '')))
self.assertTrue(distribution.os_matches(('debian', '6.0.6', ''),
('debian', '6.1', '')))
slapos.core-1.3.10/slapos/tests/__init__.py 0000644 0000000 0000000 00000000000 12517720512 017266 0 ustar root root slapos.core-1.3.10/slapos/tests/slap.py 0000644 0000000 0000000 00000125123 12517720512 016504 0 ustar root root ##############################################################################
#
# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import logging
import os
import unittest
import urlparse
import httmock
import slapos.slap
import xml_marshaller
class UndefinedYetException(Exception):
"""To catch exceptions which are not yet defined"""
class SlapMixin(unittest.TestCase):
"""
Useful methods for slap tests
"""
def setUp(self):
self._server_url = os.environ.get('TEST_SLAP_SERVER_URL', None)
if self._server_url is None:
self.server_url = 'http://localhost/'
else:
self.server_url = self._server_url
print 'Testing against SLAP server %r' % self.server_url
self.slap = slapos.slap.slap()
self.partition_id = 'PARTITION_01'
def tearDown(self):
pass
def _getTestComputerId(self):
"""
Returns the computer id used by the test
"""
return self.id()
class TestSlap(SlapMixin):
"""
Test slap against slap server
"""
def test_slap_initialisation(self):
"""
Asserts that slap initialisation works properly in case of
passing correct url
"""
slap_instance = slapos.slap.slap()
slap_instance.initializeConnection(self.server_url)
self.assertEquals(slap_instance._connection_helper.slapgrid_uri, self.server_url)
def test_slap_initialisation_ipv6_and_port(self):
"""
Asserts that slap correctly understand master_url containing
ipv6 and adds brackets if not there.
"""
slap_instance = slapos.slap.slap()
slap_instance.initializeConnection("http://1234:1234:1234:1234:1:1:1:1:5000/foo/")
self.assertEqual(
slap_instance._connection_helper.slapgrid_uri,
"http://[1234:1234:1234:1234:1:1:1:1]:5000/foo/"
)
def test_slap_initialisation_ipv6_without_port(self):
"""
Asserts that slap correctly understand master_url containing
ipv6 and adds brackets if not there.
"""
slap_instance = slapos.slap.slap()
slap_instance.initializeConnection("http://1234:1234:1234:1234:1:1:1:1/foo/")
self.assertEqual(
slap_instance._connection_helper.slapgrid_uri,
"http://[1234:1234:1234:1234:1:1:1:1]/foo/"
)
def test_slap_initialisation_ipv6_with_bracket(self):
"""
Asserts that slap correctly understand master_url containing
ipv6 and adds brackets if not there.
"""
slap_instance = slapos.slap.slap()
slap_instance.initializeConnection("http://[1234:1234:1234:1234:1:1:1:1]:5000/foo/")
self.assertEqual(
slap_instance._connection_helper.slapgrid_uri,
"http://[1234:1234:1234:1234:1:1:1:1]:5000/foo/"
)
def test_slap_initialisation_ipv4(self):
"""
Asserts that slap correctly understand master_url containing
ipv6 and adds brackets if not there.
"""
slap_instance = slapos.slap.slap()
slap_instance.initializeConnection("http://127.0.0.1:5000/foo/")
self.assertEqual(
slap_instance._connection_helper.slapgrid_uri,
"http://127.0.0.1:5000/foo/"
)
def test_slap_initialisation_hostname(self):
"""
Asserts that slap correctly understand master_url containing
ipv6 and adds brackets if not there.
"""
slap_instance = slapos.slap.slap()
slap_instance.initializeConnection("http://foo.com:5000/foo/")
self.assertEqual(
slap_instance._connection_helper.slapgrid_uri,
"http://foo.com:5000/foo/"
)
def test_registerComputer_with_new_guid(self):
"""
Asserts that calling slap.registerComputer with new guid returns
Computer object
"""
computer_guid = self._getTestComputerId()
self.slap = slapos.slap.slap()
self.slap.initializeConnection(self.server_url)
computer = self.slap.registerComputer(computer_guid)
self.assertIsInstance(computer, slapos.slap.Computer)
def test_registerComputer_with_existing_guid(self):
"""
Asserts that calling slap.registerComputer with already used guid
returns Computer object
"""
computer_guid = self._getTestComputerId()
self.slap = slapos.slap.slap()
self.slap.initializeConnection(self.server_url)
computer = self.slap.registerComputer(computer_guid)
self.assertIsInstance(computer, slapos.slap.Computer)
computer2 = self.slap.registerComputer(computer_guid)
self.assertIsInstance(computer2, slapos.slap.Computer)
# XXX: There is naming conflict in slap library.
# SoftwareRelease is currently used as suboject of Slap transmission object
def test_registerSoftwareRelease_with_new_uri(self):
"""
Asserts that calling slap.registerSoftwareRelease with new guid
returns SoftwareRelease object
"""
software_release_uri = 'http://server/' + self._getTestComputerId()
self.slap = slapos.slap.slap()
self.slap.initializeConnection(self.server_url)
software_release = self.slap.registerSoftwareRelease(software_release_uri)
self.assertIsInstance(software_release, slapos.slap.SoftwareRelease)
def test_registerSoftwareRelease_with_existing_uri(self):
"""
Asserts that calling slap.registerSoftwareRelease with already
used guid returns SoftwareRelease object
"""
software_release_uri = 'http://server/' + self._getTestComputerId()
self.slap = slapos.slap.slap()
self.slap.initializeConnection(self.server_url)
software_release = self.slap.registerSoftwareRelease(software_release_uri)
self.assertIsInstance(software_release, slapos.slap.SoftwareRelease)
software_release2 = self.slap.registerSoftwareRelease(software_release_uri)
self.assertIsInstance(software_release2, slapos.slap.SoftwareRelease)
def test_registerComputerPartition_new_partition_id_known_computer_guid(self):
"""
Asserts that calling slap.registerComputerPartition on known computer
returns ComputerPartition object
"""
computer_guid = self._getTestComputerId()
partition_id = self.partition_id
self.slap.initializeConnection(self.server_url)
self.slap.registerComputer(computer_guid)
def handler(url, req):
qs = urlparse.parse_qs(url.query)
if (url.path == '/registerComputerPartition'
and qs == {
'computer_reference': [computer_guid],
'computer_partition_reference': [partition_id]
}):
partition = slapos.slap.ComputerPartition(computer_guid, partition_id)
return {
'status_code': 200,
'content': xml_marshaller.xml_marshaller.dumps(partition)
}
else:
return {'status_code': 400}
self._handler = handler
with httmock.HTTMock(handler):
partition = self.slap.registerComputerPartition(computer_guid, partition_id)
self.assertIsInstance(partition, slapos.slap.ComputerPartition)
def test_registerComputerPartition_existing_partition_id_known_computer_guid(self):
"""
Asserts that calling slap.registerComputerPartition on known computer
returns ComputerPartition object
"""
self.test_registerComputerPartition_new_partition_id_known_computer_guid()
with httmock.HTTMock(self._handler):
partition = self.slap.registerComputerPartition(self._getTestComputerId(),
self.partition_id)
self.assertIsInstance(partition, slapos.slap.ComputerPartition)
def test_registerComputerPartition_unknown_computer_guid(self):
"""
Asserts that calling slap.registerComputerPartition on unknown
computer raises NotFoundError exception
"""
computer_guid = self._getTestComputerId()
self.slap.initializeConnection(self.server_url)
partition_id = 'PARTITION_01'
def handler(url, req):
qs = urlparse.parse_qs(url.query)
if (url.path == '/registerComputerPartition'
and qs == {
'computer_reference': [computer_guid],
'computer_partition_reference': [partition_id]
}):
return {'status_code': 404}
else:
return {'status_code': 0}
with httmock.HTTMock(handler):
self.assertRaises(slapos.slap.NotFoundError,
self.slap.registerComputerPartition,
computer_guid, partition_id)
def test_getFullComputerInformation_empty_computer_guid(self):
"""
Asserts that calling getFullComputerInformation with empty computer_id
raises early, before calling master.
"""
self.slap.initializeConnection(self.server_url)
def handler(url, req):
# Shouldn't even be called
self.assertFalse(True)
with httmock.HTTMock(handler):
self.assertRaises(slapos.slap.NotFoundError,
self.slap._connection_helper.getFullComputerInformation,
None)
def test_registerComputerPartition_empty_computer_guid(self):
"""
Asserts that calling registerComputerPartition with empty computer_id
raises early, before calling master.
"""
self.slap.initializeConnection(self.server_url)
def handler(url, req):
# Shouldn't even be called
self.assertFalse(True)
with httmock.HTTMock(handler):
self.assertRaises(slapos.slap.NotFoundError,
self.slap.registerComputerPartition,
None, 'PARTITION_01')
def test_registerComputerPartition_empty_computer_partition_id(self):
"""
Asserts that calling registerComputerPartition with empty
computer_partition_id raises early, before calling master.
"""
self.slap.initializeConnection(self.server_url)
def handler(url, req):
# Shouldn't even be called
self.assertFalse(True)
with httmock.HTTMock(handler):
self.assertRaises(slapos.slap.NotFoundError,
self.slap.registerComputerPartition,
self._getTestComputerId(), None)
def test_registerComputerPartition_empty_computer_guid_empty_computer_partition_id(self):
"""
Asserts that calling registerComputerPartition with empty
computer_partition_id raises early, before calling master.
"""
self.slap.initializeConnection(self.server_url)
def handler(url, req):
# Shouldn't even be called
self.assertFalse(True)
with httmock.HTTMock(handler):
self.assertRaises(slapos.slap.NotFoundError,
self.slap.registerComputerPartition,
None, None)
def test_getSoftwareReleaseListFromSoftwareProduct_software_product_reference(self):
"""
Check that slap.getSoftwareReleaseListFromSoftwareProduct calls
"/getSoftwareReleaseListFromSoftwareProduct" URL with correct parameters,
with software_product_reference parameter being specified.
"""
self.slap.initializeConnection(self.server_url)
software_product_reference = 'random_reference'
software_release_url_list = ['1', '2']
def handler(url, req):
qs = urlparse.parse_qs(url.query)
if (url.path == '/getSoftwareReleaseListFromSoftwareProduct'
and qs == {'software_product_reference': [software_product_reference]}):
return {
'status_code': 200,
'content': xml_marshaller.xml_marshaller.dumps(software_release_url_list)
}
with httmock.HTTMock(handler):
self.assertEqual(
self.slap.getSoftwareReleaseListFromSoftwareProduct(
software_product_reference=software_product_reference),
software_release_url_list
)
def test_getSoftwareReleaseListFromSoftwareProduct_software_release_url(self):
"""
Check that slap.getSoftwareReleaseListFromSoftwareProduct calls
"/getSoftwareReleaseListFromSoftwareProduct" URL with correct parameters,
with software_release_url parameter being specified.
"""
self.slap.initializeConnection(self.server_url)
software_release_url = 'random_url'
software_release_url_list = ['1', '2']
def handler(url, req):
qs = urlparse.parse_qs(url.query)
if (url.path == '/getSoftwareReleaseListFromSoftwareProduct'
and qs == {'software_release_url': [software_release_url]}):
return {
'status_code': 200,
'content': xml_marshaller.xml_marshaller.dumps(software_release_url_list)
}
with httmock.HTTMock(handler):
self.assertEqual(
self.slap.getSoftwareReleaseListFromSoftwareProduct(
software_release_url=software_release_url),
software_release_url_list
)
def test_getSoftwareReleaseListFromSoftwareProduct_too_many_parameters(self):
"""
Check that slap.getSoftwareReleaseListFromSoftwareProduct raises if
both parameters are set.
"""
self.assertRaises(
AttributeError,
self.slap.getSoftwareReleaseListFromSoftwareProduct, 'foo', 'bar'
)
def test_getSoftwareReleaseListFromSoftwareProduct_no_parameter(self):
"""
Check that slap.getSoftwareReleaseListFromSoftwareProduct raises if
both parameters are either not set or None.
"""
self.assertRaises(
AttributeError,
self.slap.getSoftwareReleaseListFromSoftwareProduct
)
def test_initializeConnection_getHateoasUrl(self):
"""
Test that by default, slap will try to fetch Hateoas URL from XML/RPC URL.
"""
hateoas_url = 'foo'
def handler(url, req):
qs = urlparse.parse_qs(url.query)
if (url.path == '/getHateoasUrl'):
return {
'status_code': 200,
'content': hateoas_url
}
with httmock.HTTMock(handler):
self.slap.initializeConnection('http://bar')
self.assertEqual(
self.slap._hateoas_navigator.slapos_master_hateoas_uri,
hateoas_url
)
def test_initializeConnection_specifiedHateoasUrl(self):
"""
Test that if rest URL is specified, slap will NOT try to fetch
Hateoas URL from XML/RPC URL.
"""
hateoas_url = 'foo'
def handler(url, req):
qs = urlparse.parse_qs(url.query)
if (url.path == '/getHateoasUrl'):
self.fail('slap should not have contacted master to get Hateoas URL.')
with httmock.HTTMock(handler):
self.slap.initializeConnection('http://bar', slapgrid_rest_uri=hateoas_url)
self.assertEqual(
self.slap._hateoas_navigator.slapos_master_hateoas_uri,
hateoas_url
)
def test_initializeConnection_noHateoasUrl(self):
"""
Test that if no rest URL is specified and master does not know about rest,
it still work.
"""
hateoas_url = 'foo'
def handler(url, req):
qs = urlparse.parse_qs(url.query)
if (url.path == '/getHateoasUrl'):
return {
'status_code': 404,
}
with httmock.HTTMock(handler):
self.slap.initializeConnection('http://bar')
self.assertEqual(None, getattr(self.slap, '_hateoas_navigator', None))
class TestComputer(SlapMixin):
"""
Tests slapos.slap.slap.Computer class functionality
"""
def test_computer_getComputerPartitionList_no_partition(self):
"""
Asserts that calling Computer.getComputerPartitionList without Computer
Partitions returns empty list
"""
computer_guid = self._getTestComputerId()
slap = self.slap
slap.initializeConnection(self.server_url)
def handler(url, req):
qs = urlparse.parse_qs(url.query)
if (url.path == '/registerComputerPartition'
and 'computer_reference' in qs
and 'computer_partition_reference' in qs):
slap_partition = slapos.slap.ComputerPartition(
qs['computer_reference'][0],
qs['computer_partition_reference'][0])
return {
'status_code': 200,
'content': xml_marshaller.xml_marshaller.dumps(slap_partition)
}
elif (url.path == '/getFullComputerInformation'
and 'computer_id' in qs):
slap_computer = slapos.slap.Computer(qs['computer_id'][0])
slap_computer._software_release_list = []
slap_computer._computer_partition_list = []
return {
'status_code': 200,
'content': xml_marshaller.xml_marshaller.dumps(slap_computer)
}
elif url.path == '/requestComputerPartition':
return {'status_code': 408}
else:
return {'status_code': 404}
with httmock.HTTMock(handler):
computer = self.slap.registerComputer(computer_guid)
self.assertEqual(computer.getComputerPartitionList(), [])
def _test_computer_empty_computer_guid(self, computer_method):
"""
Helper method checking if calling Computer method with empty id raises
early.
"""
self.slap.initializeConnection(self.server_url)
def handler(url, req):
# Shouldn't even be called
self.assertFalse(True)
with httmock.HTTMock(handler):
computer = self.slap.registerComputer(None)
self.assertRaises(slapos.slap.NotFoundError,
getattr(computer, computer_method))
def test_computer_getComputerPartitionList_empty_computer_guid(self):
"""
Asserts that calling getComputerPartitionList with empty
computer_guid raises early, before calling master.
"""
self._test_computer_empty_computer_guid('getComputerPartitionList')
def test_computer_getSoftwareReleaseList_empty_computer_guid(self):
"""
Asserts that calling getSoftwareReleaseList with empty
computer_guid raises early, before calling master.
"""
self._test_computer_empty_computer_guid('getSoftwareReleaseList')
def test_computer_getComputerPartitionList_only_partition(self):
"""
Asserts that calling Computer.getComputerPartitionList with only
Computer Partitions returns empty list
"""
self.computer_guid = self._getTestComputerId()
partition_id = 'PARTITION_01'
self.slap = slapos.slap.slap()
self.slap.initializeConnection(self.server_url)
def handler(url, req):
qs = urlparse.parse_qs(url.query)
if (url.path == '/registerComputerPartition'
and qs == {
'computer_reference': [self.computer_guid],
'computer_partition_reference': [partition_id]
}):
partition = slapos.slap.ComputerPartition(self.computer_guid, partition_id)
return {
'status_code': 200,
'content': xml_marshaller.xml_marshaller.dumps(partition)
}
elif (url.path == '/getFullComputerInformation'
and 'computer_id' in qs):
slap_computer = slapos.slap.Computer(qs['computer_id'][0])
slap_computer._computer_partition_list = []
return {
'status_code': 200,
'content': xml_marshaller.xml_marshaller.dumps(slap_computer)
}
else:
return {'status_code': 400}
with httmock.HTTMock(handler):
self.computer = self.slap.registerComputer(self.computer_guid)
self.partition = self.slap.registerComputerPartition(self.computer_guid,
partition_id)
self.assertEqual(self.computer.getComputerPartitionList(), [])
@unittest.skip("Not implemented")
def test_computer_reportUsage_non_valid_xml_raises(self):
"""
Asserts that calling Computer.reportUsage with non DTD
(not defined yet) XML raises (not defined yet) exception
"""
self.computer_guid = self._getTestComputerId()
self.slap = slapos.slap.slap()
self.slap.initializeConnection(self.server_url)
self.computer = self.slap.registerComputer(self.computer_guid)
non_dtd_xml = """
value
"""
self.assertRaises(UndefinedYetException,
self.computer.reportUsage,
non_dtd_xml)
@unittest.skip("Not implemented")
def test_computer_reportUsage_valid_xml_invalid_partition_raises(self):
"""
Asserts that calling Computer.reportUsage with DTD (not defined
yet) XML which refers to invalid partition raises (not defined yet)
exception
"""
self.computer_guid = self._getTestComputerId()
partition_id = 'PARTITION_01'
self.slap = slapos.slap.slap()
self.slap.initializeConnection(self.server_url)
self.computer = self.slap.registerComputer(self.computer_guid)
self.partition = self.slap.registerComputerPartition(self.computer_guid,
partition_id)
# XXX: As DTD is not defined currently proper XML is not known
bad_partition_dtd_xml = """
URL_CONNECTION_PARAMETER
""",
slap_computer_id=computer_guid,
slap_computer_partition_id=requested_partition_id)
return {
'status_code': 200,
'content': xml_marshaller.xml_marshaller.dumps(slap_partition)
}
with httmock.HTTMock(handler):
computer_partition = open_order.request(software_release_uri, 'myrefe')
self.assertIsInstance(computer_partition, slapos.slap.ComputerPartition)
self.assertEqual(requested_partition_id, computer_partition.getId())
self.assertEqual("URL_CONNECTION_PARAMETER",
computer_partition.getConnectionParameter('url'))
class TestSoftwareProductCollection(SlapMixin):
def setUp(self):
SlapMixin.setUp(self)
self.real_getSoftwareReleaseListFromSoftwareProduct =\
slapos.slap.slap.getSoftwareReleaseListFromSoftwareProduct
def fake_getSoftwareReleaseListFromSoftwareProduct(inside_self, software_product_reference):
return self.getSoftwareReleaseListFromSoftwareProduct_response
slapos.slap.slap.getSoftwareReleaseListFromSoftwareProduct =\
fake_getSoftwareReleaseListFromSoftwareProduct
self.product_collection = slapos.slap.SoftwareProductCollection(
logging.getLogger(), slapos.slap.slap())
def tearDown(self):
slapos.slap.slap.getSoftwareReleaseListFromSoftwareProduct =\
self.real_getSoftwareReleaseListFromSoftwareProduct
def test_get_product(self):
"""
Test that the get method (aliased to __getattr__) returns the first element
of the list given by getSoftwareReleaseListFromSoftwareProduct (i.e the
best one).
"""
self.getSoftwareReleaseListFromSoftwareProduct_response = ['0', '1', '2']
self.assertEqual(
self.product_collection.get('random_reference'),
self.getSoftwareReleaseListFromSoftwareProduct_response[0]
)
def test_get_product_empty_product(self):
"""
Test that the get method (aliased to __getattr__) raises if no
Software Release is related to the Software Product, or if the
Software Product does not exist.
"""
self.getSoftwareReleaseListFromSoftwareProduct_response = []
self.assertRaises(
AttributeError,
self.product_collection.get, 'random_reference',
)
def test_get_product_getattr(self):
"""
Test that __getattr__ method is bound to get() method.
"""
self.getSoftwareReleaseListFromSoftwareProduct_response = ['0']
self.product_collection.foo
self.assertEqual(
self.product_collection.__getattr__,
self.product_collection.get
)
self.assertEqual(self.product_collection.foo, '0')
if __name__ == '__main__':
print 'You can point to any SLAP server by setting TEST_SLAP_SERVER_URL '\
'environment variable'
unittest.main()
slapos.core-1.3.10/slapos/tests/cli.py 0000644 0000000 0000000 00000013507 12517720512 016316 0 ustar root root ##############################################################################
#
# Copyright (c) 2013 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import logging
import pprint
import unittest
from mock import patch, create_autospec
import slapos.cli.list
import slapos.cli.info
import slapos.cli.supervisorctl
from slapos.client import ClientConfig
import slapos.grid.svcbackend
import slapos.proxy
import slapos.slap
import supervisor.supervisorctl
def raiseNotFoundError(*args, **kwargs):
raise slapos.slap.NotFoundError()
class CliMixin(unittest.TestCase):
def setUp(self):
slap = slapos.slap.slap()
self.local = {'slap': slap}
self.logger = create_autospec(logging.Logger)
self.conf = create_autospec(ClientConfig)
class TestCliProxy(CliMixin):
def test_generateSoftwareProductListFromString(self):
"""
Test that generateSoftwareProductListFromString correctly parses a parameter
coming from the configuration file.
"""
software_product_list_string = """
product1 url1
product2 url2"""
software_release_url_list = {
'product1': 'url1',
'product2': 'url2',
}
self.assertEqual(
slapos.proxy._generateSoftwareProductListFromString(
software_product_list_string),
software_release_url_list
)
def test_generateSoftwareProductListFromString_emptyString(self):
self.assertEqual(
slapos.proxy._generateSoftwareProductListFromString(''),
{}
)
class TestCliList(CliMixin):
def test_list(self):
"""
Test "slapos list" command output.
"""
return_value = {
'instance1': slapos.slap.SoftwareInstance(_title='instance1', _software_release_url='SR1'),
'instance2': slapos.slap.SoftwareInstance(_title='instance2', _software_release_url='SR2'),
}
with patch.object(slapos.slap.slap, 'getOpenOrderDict', return_value=return_value) as _:
slapos.cli.list.do_list(self.logger, None, self.local)
self.logger.info.assert_any_call('%s %s', 'instance1', 'SR1')
self.logger.info.assert_any_call('%s %s', 'instance2', 'SR2')
def test_emptyList(self):
with patch.object(slapos.slap.slap, 'getOpenOrderDict', return_value={}) as _:
slapos.cli.list.do_list(self.logger, None, self.local)
self.logger.info.assert_called_once_with('No existing service.')
@patch.object(slapos.slap.slap, 'registerOpenOrder', return_value=slapos.slap.OpenOrder())
class TestCliInfo(CliMixin):
def test_info(self, _):
"""
Test "slapos info" command output.
"""
setattr(self.conf, 'reference', 'instance1')
instance = slapos.slap.SoftwareInstance(
_software_release_url='SR1',
_requested_state = 'mystate',
_connection_dict = {'myconnectionparameter': 'value1'},
_parameter_dict = {'myinstanceparameter': 'value2'}
)
with patch.object(slapos.slap.OpenOrder, 'getInformation', return_value=instance):
slapos.cli.info.do_info(self.logger, self.conf, self.local)
self.logger.info.assert_any_call(pprint.pformat(instance._parameter_dict))
self.logger.info.assert_any_call('Software Release URL: %s', instance._software_release_url)
self.logger.info.assert_any_call('Instance state: %s', instance._requested_state)
self.logger.info.assert_any_call(pprint.pformat(instance._parameter_dict))
self.logger.info.assert_any_call(pprint.pformat(instance._connection_dict))
def test_unknownReference(self, _):
"""
Test "slapos info" command output in case reference
of service is not known.
"""
setattr(self.conf, 'reference', 'instance1')
with patch.object(slapos.slap.OpenOrder, 'getInformation', side_effect=raiseNotFoundError):
slapos.cli.info.do_info(self.logger, self.conf, self.local)
self.logger.warning.assert_called_once_with('Instance %s does not exist.', self.conf.reference)
@patch.object(supervisor.supervisorctl, 'main')
class TestCliSupervisorctl(CliMixin):
def test_allow_supervisord_launch(self, _):
"""
Test that "slapos node supervisorctl" tries to launch supervisord
"""
instance_root = '/foo/bar'
with patch.object(slapos.grid.svcbackend, 'launchSupervisord') as launchSupervisord:
slapos.cli.supervisorctl.do_supervisorctl(self.logger, instance_root, ['status'], False)
launchSupervisord.assert_any_call(instance_root=instance_root, logger=self.logger)
def test_forbid_supervisord_launch(self, _):
"""
Test that "slapos node supervisorctl" does not try to launch supervisord
"""
instance_root = '/foo/bar'
with patch.object(slapos.grid.svcbackend, 'launchSupervisord') as launchSupervisord:
slapos.cli.supervisorctl.do_supervisorctl(self.logger, instance_root, ['status'], True)
self.assertFalse(launchSupervisord.called)
slapos.core-1.3.10/slapos/tests/configure_local.py 0000644 0000000 0000000 00000012763 12517720512 020705 0 ustar root root ##############################################################################
#
# Copyright (c) 2014 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import os
import unittest
import shutil
import tempfile
import slapos.slap
import slapos.cli.configure_local
from slapos.cli.configure_local import ConfigureLocalCommand, _createConfigurationDirectory
from slapos.cli.entry import SlapOSApp
from argparse import Namespace
from ConfigParser import ConfigParser
# Disable any command to launch slapformat and supervisor
slapos.cli.configure_local._runFormat = lambda x: "Do nothing"
slapos.cli.configure_local.launchSupervisord = lambda instance_root, logger: "Do nothing"
class TestConfigureLocal(unittest.TestCase):
def setUp(self):
self.slap = slapos.slap.slap()
self.app = SlapOSApp()
self.temp_dir = tempfile.mkdtemp()
os.environ["HOME"] = self.temp_dir
self.instance_root = tempfile.mkdtemp()
self.software_root = tempfile.mkdtemp()
if os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
def tearDown(self):
for temp_path in (self.temp_dir, \
self.instance_root, self.software_root):
if os.path.exists(temp_path):
shutil.rmtree(temp_path)
def test_configure_local_environment_with_default_value(self):
config = ConfigureLocalCommand(self.app, Namespace())
config.__dict__.update({i.dest: i.default \
for i in config.get_parser(None)._option_string_actions.values()})
config.slapos_configuration_directory = self.temp_dir
config.slapos_buildout_directory = self.temp_dir
config.slapos_instance_root = self.instance_root
slapos.cli.configure_local.do_configure(
config, config.fetch_config, self.app.log)
expected_software_root = "/opt/slapgrid"
self.assertTrue(
os.path.exists("%s/.slapos/slapos-client.cfg" % self.temp_dir))
with open(self.temp_dir + '/slapos-proxy.cfg') as fout:
proxy_config = ConfigParser()
proxy_config.readfp(fout)
self.assertEquals(proxy_config.get('slapos', 'instance_root'),
self.instance_root)
self.assertEquals(proxy_config.get('slapos', 'software_root'),
expected_software_root)
with open(self.temp_dir + '/slapos.cfg') as fout:
proxy_config = ConfigParser()
proxy_config.readfp(fout)
self.assertEquals(proxy_config.get('slapos', 'instance_root'),
self.instance_root)
self.assertEquals(proxy_config.get('slapos', 'software_root'),
expected_software_root)
def test_configure_local_environment(self):
config = ConfigureLocalCommand(self.app, Namespace())
config.__dict__.update({i.dest: i.default \
for i in config.get_parser(None)._option_string_actions.values()})
config.slapos_configuration_directory = self.temp_dir
config.slapos_buildout_directory = self.temp_dir
config.slapos_instance_root = self.instance_root
config.slapos_software_root = self.software_root
slapos.cli.configure_local.do_configure(
config, config.fetch_config, self.app.log)
log_folder = os.path.join(config.slapos_buildout_directory, 'log')
self.assertTrue(os.path.exists(log_folder), "%s not exists" % log_folder)
self.assertTrue(
os.path.exists("%s/.slapos/slapos-client.cfg" % self.temp_dir))
with open(self.temp_dir + '/slapos-proxy.cfg') as fout:
proxy_config = ConfigParser()
proxy_config.readfp(fout)
self.assertEquals(proxy_config.get('slapos', 'instance_root'),
self.instance_root)
self.assertEquals(proxy_config.get('slapos', 'software_root'),
self.software_root)
with open(self.temp_dir + '/slapos.cfg') as fout:
proxy_config = ConfigParser()
proxy_config.readfp(fout)
self.assertEquals(proxy_config.get('slapos', 'instance_root'),
self.instance_root)
self.assertEquals(proxy_config.get('slapos', 'software_root'),
self.software_root)
log_file = proxy_config.get('slapformat', 'log_file')
self.assertTrue(log_file.startswith(log_folder),
"%s don't starts with %s" % (log_file, log_folder))
slapos.core-1.3.10/slapos/tests/slapgrid.py 0000644 0000000 0000000 00000240036 12517720512 017353 0 ustar root root ##############################################################################
#
# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from __future__ import absolute_import
import logging
import os
import random
import shutil
import signal
import socket
import sys
import stat
import tempfile
import textwrap
import time
import unittest
import urlparse
import xml_marshaller
from mock import patch
import slapos.slap.slap
import slapos.grid.utils
from slapos.grid import slapgrid
from slapos.grid.utils import md5digest
from slapos.grid.watchdog import Watchdog
from slapos.grid import SlapObject
from slapos.grid.SlapObject import WATCHDOG_MARK
import slapos.grid.SlapObject
import httmock
dummylogger = logging.getLogger()
WATCHDOG_TEMPLATE = """#!{python_path} -S
import sys
sys.path={sys_path}
import slapos.slap
import slapos.grid.watchdog
def bang(self_partition, message):
nl = chr(10)
with open('{watchdog_banged}', 'w') as fout:
for key, value in vars(self_partition).items():
fout.write('%s: %s%s' % (key, value, nl))
if key == '_connection_helper':
for k, v in vars(value).items():
fout.write(' %s: %s%s' % (k, v, nl))
fout.write(message)
slapos.slap.ComputerPartition.bang = bang
slapos.grid.watchdog.main()
"""
WRAPPER_CONTENT = """#!/bin/sh
touch worked &&
mkdir -p etc/run &&
echo "#!/bin/sh" > etc/run/wrapper &&
echo "while true; do echo Working; sleep 0.1; done" >> etc/run/wrapper &&
chmod 755 etc/run/wrapper
"""
DAEMON_CONTENT = """#!/bin/sh
mkdir -p etc/service &&
echo "#!/bin/sh" > etc/service/daemon &&
echo "touch launched
if [ -f ./crashed ]; then
while true; do echo Working; sleep 0.1; done
else
touch ./crashed; echo Failing; sleep 1; exit 111;
fi" >> etc/service/daemon &&
chmod 755 etc/service/daemon &&
touch worked
"""
class BasicMixin(object):
def setUp(self):
self._tempdir = tempfile.mkdtemp()
self.software_root = os.path.join(self._tempdir, 'software')
self.instance_root = os.path.join(self._tempdir, 'instance')
logging.basicConfig(level=logging.DEBUG)
self.setSlapgrid()
def setSlapgrid(self, develop=False):
if getattr(self, 'master_url', None) is None:
self.master_url = 'http://127.0.0.1:80/'
self.computer_id = 'computer'
self.supervisord_socket = os.path.join(self._tempdir, 'supervisord.sock')
self.supervisord_configuration_path = os.path.join(self._tempdir,
'supervisord')
self.usage_report_periodicity = 1
self.buildout = None
self.grid = slapgrid.Slapgrid(self.software_root,
self.instance_root,
self.master_url,
self.computer_id,
self.buildout,
develop=develop,
logger=logging.getLogger())
# monkey patch buildout bootstrap
def dummy(*args, **kw):
pass
slapos.grid.utils.bootstrapBuildout = dummy
SlapObject.PROGRAM_PARTITION_TEMPLATE = textwrap.dedent("""\
[program:%(program_id)s]
directory=%(program_directory)s
command=%(program_command)s
process_name=%(program_name)s
autostart=false
autorestart=false
startsecs=0
startretries=0
exitcodes=0
stopsignal=TERM
stopwaitsecs=60
stopasgroup=true
killasgroup=true
user=%(user_id)s
group=%(group_id)s
serverurl=AUTO
redirect_stderr=true
stdout_logfile=%(instance_path)s/.%(program_id)s.log
stderr_logfile=%(instance_path)s/.%(program_id)s.log
environment=USER="%(USER)s",LOGNAME="%(USER)s",HOME="%(HOME)s"
""")
def launchSlapgrid(self, develop=False):
self.setSlapgrid(develop=develop)
return self.grid.processComputerPartitionList()
def launchSlapgridSoftware(self, develop=False):
self.setSlapgrid(develop=develop)
return self.grid.processSoftwareReleaseList()
def assertLogContent(self, log_path, expected, tries=50):
for i in range(tries):
if expected in open(log_path).read():
return
time.sleep(0.1)
self.fail('%r not found in %s' % (expected, log_path))
def assertIsCreated(self, path, tries=50):
for i in range(tries):
if os.path.exists(path):
return
time.sleep(0.1)
self.fail('%s should be created' % path)
def assertIsNotCreated(self, path, tries=50):
for i in range(tries):
if os.path.exists(path):
self.fail('%s should not be created' % path)
time.sleep(0.1)
def assertInstanceDirectoryListEqual(self, instance_list):
instance_list.append('etc')
instance_list.append('var')
instance_list.append('supervisord.socket')
self.assertItemsEqual(os.listdir(self.instance_root), instance_list)
def tearDown(self):
# XXX: Hardcoded pid, as it is not configurable in slapos
svc = os.path.join(self.instance_root, 'var', 'run', 'supervisord.pid')
if os.path.exists(svc):
try:
pid = int(open(svc).read().strip())
except ValueError:
pass
else:
os.kill(pid, signal.SIGTERM)
shutil.rmtree(self._tempdir, True)
class TestRequiredOnlyPartitions(unittest.TestCase):
def test_no_errors(self):
required = ['one', 'three']
existing = ['one', 'two', 'three']
slapgrid.check_required_only_partitions(existing, required)
def test_one_missing(self):
required = ['foobar', 'two', 'one']
existing = ['one', 'two', 'three']
self.assertRaisesRegexp(ValueError,
'Unknown partition: foobar',
slapgrid.check_required_only_partitions,
existing, required)
def test_several_missing(self):
required = ['foobar', 'barbaz']
existing = ['one', 'two', 'three']
self.assertRaisesRegexp(ValueError,
'Unknown partitions: barbaz, foobar',
slapgrid.check_required_only_partitions,
existing, required)
class TestBasicSlapgridCP(BasicMixin, unittest.TestCase):
def test_no_software_root(self):
self.assertRaises(OSError, self.grid.processComputerPartitionList)
def test_no_instance_root(self):
os.mkdir(self.software_root)
self.assertRaises(OSError, self.grid.processComputerPartitionList)
@unittest.skip('which request handler here?')
def test_no_master(self):
os.mkdir(self.software_root)
os.mkdir(self.instance_root)
self.assertRaises(socket.error, self.grid.processComputerPartitionList)
class MasterMixin(BasicMixin):
def _mock_sleep(self):
self.fake_waiting_time = None
self.real_sleep = time.sleep
def mocked_sleep(secs):
if self.fake_waiting_time is not None:
secs = self.fake_waiting_time
self.real_sleep(secs)
time.sleep = mocked_sleep
def _unmock_sleep(self):
time.sleep = self.real_sleep
def setUp(self):
self._mock_sleep()
BasicMixin.setUp(self)
def tearDown(self):
self._unmock_sleep()
BasicMixin.tearDown(self)
class ComputerForTest(object):
"""
Class to set up environment for tests setting instance, software
and server response
"""
def __init__(self,
software_root,
instance_root,
instance_amount=1,
software_amount=1):
"""
Will set up instances, software and sequence
"""
self.sequence = []
self.instance_amount = instance_amount
self.software_amount = software_amount
self.software_root = software_root
self.instance_root = instance_root
if not os.path.isdir(self.instance_root):
os.mkdir(self.instance_root)
if not os.path.isdir(self.software_root):
os.mkdir(self.software_root)
self.setSoftwares()
self.setInstances()
def request_handler(self, url, req):
"""
Define _callback.
Will register global sequence of message, sequence by partition
and error and error message by partition
"""
self.sequence.append(url.path)
if req.method == 'GET':
qs = urlparse.parse_qs(url.query)
else:
qs = urlparse.parse_qs(req.body)
if (url.path == '/getFullComputerInformation'
and 'computer_id' in qs):
slap_computer = self.getComputer(qs['computer_id'][0])
return {
'status_code': 200,
'content': xml_marshaller.xml_marshaller.dumps(slap_computer)
}
if req.method == 'POST' and 'computer_partition_id' in qs:
instance = self.instance_list[int(qs['computer_partition_id'][0])]
instance.sequence.append(url.path)
instance.header_list.append(req.headers)
if url.path == '/availableComputerPartition':
return {'status_code': 200}
if url.path == '/startedComputerPartition':
instance.state = 'started'
return {'status_code': 200}
if url.path == '/stoppedComputerPartition':
instance.state = 'stopped'
return {'status_code': 200}
if url.path == '/destroyedComputerPartition':
instance.state = 'destroyed'
return {'status_code': 200}
if url.path == '/softwareInstanceBang':
return {'status_code': 200}
if url.path == '/softwareInstanceError':
instance.error_log = '\n'.join(
[
line
for line in qs['error_log'][0].splitlines()
if 'dropPrivileges' not in line
]
)
instance.error = True
return {'status_code': 200}
elif req.method == 'POST' and 'url' in qs:
# XXX hardcoded to first software release!
software = self.software_list[0]
software.sequence.append(url.path)
if url.path == '/buildingSoftwareRelease':
return {'status_code': 200}
if url.path == '/softwareReleaseError':
software.error_log = '\n'.join(
[
line
for line in qs['error_log'][0].splitlines()
if 'dropPrivileges' not in line
]
)
software.error = True
return {'status_code': 200}
else:
return {'status_code': 500}
def setSoftwares(self):
"""
Will set requested amount of software
"""
self.software_list = [
SoftwareForTest(self.software_root, name=str(i))
for i in range(self.software_amount)
]
def setInstances(self):
"""
Will set requested amount of instance giving them by default first software
"""
if self.software_list:
software = self.software_list[0]
else:
software = None
self.instance_list = [
InstanceForTest(self.instance_root, name=str(i), software=software)
for i in range(self.instance_amount)
]
def getComputer(self, computer_id):
"""
Will return current requested state of computer
"""
slap_computer = slapos.slap.Computer(computer_id)
slap_computer._software_release_list = [
software.getSoftware(computer_id)
for software in self.software_list
]
slap_computer._computer_partition_list = [
instance.getInstance(computer_id)
for instance in self.instance_list
]
return slap_computer
class InstanceForTest(object):
"""
Class containing all needed paramaters and function to simulate instances
"""
def __init__(self, instance_root, name, software):
self.instance_root = instance_root
self.software = software
self.requested_state = 'stopped'
self.state = None
self.error = False
self.error_log = None
self.sequence = []
self.header_list = []
self.name = name
self.partition_path = os.path.join(self.instance_root, self.name)
os.mkdir(self.partition_path, 0o750)
self.timestamp = None
def getInstance(self, computer_id):
"""
Will return current requested state of instance
"""
partition = slapos.slap.ComputerPartition(computer_id, self.name)
partition._software_release_document = self.getSoftwareRelease()
partition._requested_state = self.requested_state
if getattr(self, 'filter_dict', None):
partition._filter_dict = self.filter_dict
if self.software is not None:
if self.timestamp is not None:
partition._parameter_dict = {'timestamp': self.timestamp}
return partition
def getSoftwareRelease(self):
"""
Return software release for Instance
"""
if self.software is not None:
sr = slapos.slap.SoftwareRelease()
sr._software_release = self.software.name
return sr
else:
return None
def setPromise(self, promise_name, promise_content):
"""
This function will set promise and return its path
"""
promise_path = os.path.join(self.partition_path, 'etc', 'promise')
if not os.path.isdir(promise_path):
os.makedirs(promise_path)
promise = os.path.join(promise_path, promise_name)
open(promise, 'w').write(promise_content)
os.chmod(promise, 0o777)
def setCertificate(self, certificate_repository_path):
if not os.path.exists(certificate_repository_path):
os.mkdir(certificate_repository_path)
self.cert_file = os.path.join(certificate_repository_path,
"%s.crt" % self.name)
self.certificate = str(random.random())
open(self.cert_file, 'w').write(self.certificate)
self.key_file = os.path.join(certificate_repository_path,
'%s.key' % self.name)
self.key = str(random.random())
open(self.key_file, 'w').write(self.key)
class SoftwareForTest(object):
"""
Class to prepare and simulate software.
each instance has a sotfware attributed
"""
def __init__(self, software_root, name=''):
"""
Will set file and variable for software
"""
self.software_root = software_root
self.name = 'http://sr%s/' % name
self.sequence = []
self.software_hash = md5digest(self.name)
self.srdir = os.path.join(self.software_root, self.software_hash)
self.requested_state = 'available'
os.mkdir(self.srdir)
self.setTemplateCfg()
self.srbindir = os.path.join(self.srdir, 'bin')
os.mkdir(self.srbindir)
self.setBuildout()
def getSoftware(self, computer_id):
"""
Will return current requested state of software
"""
software = slapos.slap.SoftwareRelease(self.name, computer_id)
software._requested_state = self.requested_state
return software
def setTemplateCfg(self, template="""[buildout]"""):
"""
Set template.cfg
"""
open(os.path.join(self.srdir, 'template.cfg'), 'w').write(template)
def setBuildout(self, buildout="""#!/bin/sh
touch worked"""):
"""
Set a buildout exec in bin
"""
open(os.path.join(self.srbindir, 'buildout'), 'w').write(buildout)
os.chmod(os.path.join(self.srbindir, 'buildout'), 0o755)
def setPeriodicity(self, periodicity):
"""
Set a periodicity file
"""
with open(os.path.join(self.srdir, 'periodicity'), 'w') as fout:
fout.write(str(periodicity))
class TestSlapgridCPWithMaster(MasterMixin, unittest.TestCase):
def test_nothing_to_do(self):
computer = ComputerForTest(self.software_root, self.instance_root, 0, 0)
with httmock.HTTMock(computer.request_handler):
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual([])
self.assertItemsEqual(os.listdir(self.software_root), [])
st = os.stat(os.path.join(self.instance_root, 'var'))
self.assertEquals(stat.S_IMODE(st.st_mode), 0o755)
def test_one_partition(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
partition = os.path.join(self.instance_root, '0')
self.assertItemsEqual(os.listdir(partition), ['.slapgrid', 'buildout.cfg',
'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation', '/availableComputerPartition',
'/stoppedComputerPartition'])
def test_one_partition_instance_cfg(self):
"""
Check that slapgrid processes instance is profile is not named
"template.cfg" but "instance.cfg".
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
partition = os.path.join(self.instance_root, '0')
self.assertItemsEqual(os.listdir(partition), ['.slapgrid', 'buildout.cfg',
'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation', '/availableComputerPartition',
'/stoppedComputerPartition'])
def test_one_free_partition(self):
"""
Test if slapgrid cp does not process "free" partition
"""
computer = ComputerForTest(self.software_root,
self.instance_root,
software_amount=0)
with httmock.HTTMock(computer.request_handler):
partition = computer.instance_list[0]
partition.requested_state = 'destroyed'
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(partition.partition_path), [])
self.assertItemsEqual(os.listdir(self.software_root), [])
self.assertEqual(partition.sequence, [])
def test_one_partition_started(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
partition = computer.instance_list[0]
partition.requested_state = 'started'
partition.software.setBuildout(WRAPPER_CONTENT)
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(partition.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
wrapper_log = os.path.join(partition.partition_path, '.0_wrapper.log')
self.assertLogContent(wrapper_log, 'Working')
self.assertItemsEqual(os.listdir(self.software_root), [partition.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation', '/availableComputerPartition',
'/startedComputerPartition'])
self.assertEqual(partition.state, 'started')
def test_one_partition_started_stopped(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
instance.software.setBuildout("""#!/bin/sh
touch worked &&
mkdir -p etc/run &&
(
cat <<'HEREDOC'
#!%(python)s
import signal
def handler(signum, frame):
print 'Signal handler called with signal', signum
raise SystemExit
signal.signal(signal.SIGTERM, handler)
while True:
print "Working"
HEREDOC
)> etc/run/wrapper &&
chmod 755 etc/run/wrapper
""" % {'python': sys.executable})
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertLogContent(wrapper_log, 'Working')
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation', '/availableComputerPartition',
'/startedComputerPartition'])
self.assertEqual(instance.state, 'started')
computer.sequence = []
instance.requested_state = 'stopped'
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertLogContent(wrapper_log, 'Signal handler called with signal 15')
self.assertEqual(computer.sequence,
['/getHateoasUrl', '/getFullComputerInformation', '/availableComputerPartition',
'/stoppedComputerPartition'])
self.assertEqual(instance.state, 'stopped')
def test_one_broken_partition_stopped(self):
"""
Check that, for, an already started instance if stop is requested,
processes will be stopped even if instance is broken (buildout fails
to run) but status is still started.
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
instance.software.setBuildout("""#!/bin/sh
touch worked &&
mkdir -p etc/run &&
(
cat <<'HEREDOC'
#!%(python)s
import signal
def handler(signum, frame):
print 'Signal handler called with signal', signum
raise SystemExit
signal.signal(signal.SIGTERM, handler)
while True:
print "Working"
HEREDOC
)> etc/run/wrapper &&
chmod 755 etc/run/wrapper
""" % {'python': sys.executable})
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertLogContent(wrapper_log, 'Working')
self.assertItemsEqual(os.listdir(self.software_root),
[instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation', '/availableComputerPartition',
'/startedComputerPartition'])
self.assertEqual(instance.state, 'started')
computer.sequence = []
instance.requested_state = 'stopped'
instance.software.setBuildout("""#!/bin/sh
exit 1
""")
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_FAIL)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertLogContent(wrapper_log, 'Signal handler called with signal 15')
self.assertEqual(computer.sequence,
['/getHateoasUrl', '/getFullComputerInformation',
'/softwareInstanceError'])
self.assertEqual(instance.state, 'started')
def test_one_partition_stopped_started(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'stopped'
instance.software.setBuildout(WRAPPER_CONTENT)
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
partition = os.path.join(self.instance_root, '0')
self.assertItemsEqual(os.listdir(partition),
['.slapgrid', 'buildout.cfg', 'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertItemsEqual(os.listdir(self.software_root),
[instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation', '/availableComputerPartition',
'/stoppedComputerPartition'])
self.assertEqual('stopped', instance.state)
instance.requested_state = 'started'
computer.sequence = []
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
partition = os.path.join(self.instance_root, '0')
self.assertItemsEqual(os.listdir(partition),
['.slapgrid', '.0_wrapper.log', 'etc',
'buildout.cfg', 'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertItemsEqual(os.listdir(self.software_root),
[instance.software.software_hash])
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertLogContent(wrapper_log, 'Working')
self.assertEqual(computer.sequence,
['/getHateoasUrl', '/getFullComputerInformation', '/availableComputerPartition',
'/startedComputerPartition'])
self.assertEqual('started', instance.state)
def test_one_partition_destroyed(self):
"""
Test that an existing partition with "destroyed" status will only be
stopped by slapgrid-cp, not processed
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'destroyed'
dummy_file_name = 'dummy_file'
with open(os.path.join(instance.partition_path, dummy_file_name), 'w') as dummy_file:
dummy_file.write('dummy')
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
partition = os.path.join(self.instance_root, '0')
self.assertItemsEqual(os.listdir(partition), ['.slapgrid', dummy_file_name])
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/stoppedComputerPartition'])
self.assertEqual('stopped', instance.state)
class TestSlapgridCPWithMasterWatchdog(MasterMixin, unittest.TestCase):
def setUp(self):
MasterMixin.setUp(self)
# Prepare watchdog
self.watchdog_banged = os.path.join(self._tempdir, 'watchdog_banged')
watchdog_path = os.path.join(self._tempdir, 'watchdog')
open(watchdog_path, 'w').write(WATCHDOG_TEMPLATE.format(
python_path=sys.executable,
sys_path=sys.path,
watchdog_banged=self.watchdog_banged
))
os.chmod(watchdog_path, 0o755)
self.grid.watchdog_path = watchdog_path
slapos.grid.slapgrid.WATCHDOG_PATH = watchdog_path
def test_one_failing_daemon_in_service_will_bang_with_watchdog(self):
"""
Check that a failing service watched by watchdog trigger bang
1.Prepare computer and set a service named daemon in etc/service
(to be watched by watchdog). This daemon will fail.
2.Prepare file for supervisord to call watchdog
-Set sys.path
-Monkeypatch computer partition bang
3.Check damemon is launched
4.Wait for it to fail
5.Wait for file generated by monkeypacthed bang to appear
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
partition = computer.instance_list[0]
partition.requested_state = 'started'
partition.software.setBuildout(DAEMON_CONTENT)
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(partition.partition_path),
['.slapgrid', '.0_daemon.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
daemon_log = os.path.join(partition.partition_path, '.0_daemon.log')
self.assertLogContent(daemon_log, 'Failing')
self.assertIsCreated(self.watchdog_banged)
self.assertIn('daemon', open(self.watchdog_banged).read())
def test_one_failing_daemon_in_run_will_not_bang_with_watchdog(self):
"""
Check that a failing service watched by watchdog does not trigger bang
1.Prepare computer and set a service named daemon in etc/run
(not watched by watchdog). This daemon will fail.
2.Prepare file for supervisord to call watchdog
-Set sys.path
-Monkeypatch computer partition bang
3.Check damemon is launched
4.Wait for it to fail
5.Check that file generated by monkeypacthed bang do not appear
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
partition = computer.instance_list[0]
partition.requested_state = 'started'
# Content of run wrapper
WRAPPER_CONTENT = textwrap.dedent("""#!/bin/sh
touch ./launched
touch ./crashed
echo Failing
sleep 1
exit 111
""")
BUILDOUT_RUN_CONTENT = textwrap.dedent("""#!/bin/sh
mkdir -p etc/run &&
echo "%s" >> etc/run/daemon &&
chmod 755 etc/run/daemon &&
touch worked
""" % WRAPPER_CONTENT)
partition.software.setBuildout(BUILDOUT_RUN_CONTENT)
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(partition.partition_path),
['.slapgrid', '.0_daemon.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
daemon_log = os.path.join(partition.partition_path, '.0_daemon.log')
self.assertLogContent(daemon_log, 'Failing')
self.assertIsNotCreated(self.watchdog_banged)
def test_watched_by_watchdog_bang(self):
"""
Test that a process going to fatal or exited mode in supervisord
is banged if watched by watchdog
Certificates used for the bang are also checked
(ie: watchdog id in process name)
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
certificate_repository_path = os.path.join(self._tempdir, 'partition_pki')
instance.setCertificate(certificate_repository_path)
watchdog = Watchdog(
master_url='https://127.0.0.1/',
computer_id=self.computer_id,
certificate_repository_path=certificate_repository_path
)
for event in watchdog.process_state_events:
instance.sequence = []
instance.header_list = []
headers = {'eventname': event}
payload = 'processname:%s groupname:%s from_state:RUNNING' % (
'daemon' + WATCHDOG_MARK, instance.name)
watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, ['/softwareInstanceBang'])
def test_unwanted_events_will_not_bang(self):
"""
Test that a process going to a mode not watched by watchdog
in supervisord is not banged if watched by watchdog
"""
computer = ComputerForTest(self.software_root, self.instance_root)
instance = computer.instance_list[0]
watchdog = Watchdog(
master_url=self.master_url,
computer_id=self.computer_id,
certificate_repository_path=None
)
for event in ['EVENT', 'PROCESS_STATE', 'PROCESS_STATE_RUNNING',
'PROCESS_STATE_BACKOFF', 'PROCESS_STATE_STOPPED']:
computer.sequence = []
headers = {'eventname': event}
payload = 'processname:%s groupname:%s from_state:RUNNING' % (
'daemon' + WATCHDOG_MARK, instance.name)
watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, [])
def test_not_watched_by_watchdog_do_not_bang(self):
"""
Test that a process going to fatal or exited mode in supervisord
is not banged if not watched by watchdog
(ie: no watchdog id in process name)
"""
computer = ComputerForTest(self.software_root, self.instance_root)
instance = computer.instance_list[0]
watchdog = Watchdog(
master_url=self.master_url,
computer_id=self.computer_id,
certificate_repository_path=None
)
for event in watchdog.process_state_events:
computer.sequence = []
headers = {'eventname': event}
payload = "processname:%s groupname:%s from_state:RUNNING"\
% ('daemon', instance.name)
watchdog.handle_event(headers, payload)
self.assertEqual(computer.sequence, [])
def test_watchdog_create_bang_file_after_bang(self):
"""
For a partition that has been successfully deployed (thus .timestamp file
existing), check that bang file is created and contains the timestamp of
.timestamp file.
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
certificate_repository_path = os.path.join(self._tempdir, 'partition_pki')
instance.setCertificate(certificate_repository_path)
partition = os.path.join(self.instance_root, '0')
timestamp_content = '1234'
timestamp_file = open(os.path.join(partition, slapos.grid.slapgrid.COMPUTER_PARTITION_TIMESTAMP_FILENAME), 'w')
timestamp_file.write(timestamp_content)
timestamp_file.close()
watchdog = Watchdog(
master_url='https://127.0.0.1/',
computer_id=self.computer_id,
certificate_repository_path=certificate_repository_path,
instance_root_path=self.instance_root
)
event = watchdog.process_state_events[0]
instance.sequence = []
instance.header_list = []
headers = {'eventname': event}
payload = 'processname:%s groupname:%s from_state:RUNNING' % (
'daemon' + WATCHDOG_MARK, instance.name)
watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, ['/softwareInstanceBang'])
self.assertEqual(open(os.path.join(partition, slapos.grid.slapgrid.COMPUTER_PARTITION_LATEST_BANG_TIMESTAMP_FILENAME)).read(), timestamp_content)
def test_watchdog_ignore_bang_if_partition_not_deployed(self):
"""
For a partition that has never been successfully deployed (buildout is
failing, promise is not passing, etc), test that bang is ignored.
Practically speaking, .timestamp file in the partition does not exsit.
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
certificate_repository_path = os.path.join(self._tempdir, 'partition_pki')
instance.setCertificate(certificate_repository_path)
partition = os.path.join(self.instance_root, '0')
timestamp_content = '1234'
watchdog = Watchdog(
master_url='https://127.0.0.1/',
computer_id=self.computer_id,
certificate_repository_path=certificate_repository_path,
instance_root_path=self.instance_root
)
event = watchdog.process_state_events[0]
instance.sequence = []
instance.header_list = []
headers = {'eventname': event}
payload = 'processname:%s groupname:%s from_state:RUNNING' % (
'daemon' + WATCHDOG_MARK, instance.name)
watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, ['/softwareInstanceBang'])
self.assertNotEqual(open(os.path.join(partition, slapos.grid.slapgrid.COMPUTER_PARTITION_LATEST_BANG_TIMESTAMP_FILENAME)).read(), timestamp_content)
def test_watchdog_bang_only_once_if_partition_never_deployed(self):
"""
For a partition that has been never successfully deployed (promises are not passing,
etc), test that:
* First bang is transmitted
* subsequent bangs are ignored until a deployment is successful.
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
certificate_repository_path = os.path.join(self._tempdir, 'partition_pki')
instance.setCertificate(certificate_repository_path)
partition = os.path.join(self.instance_root, '0')
watchdog = Watchdog(
master_url='https://127.0.0.1/',
computer_id=self.computer_id,
certificate_repository_path=certificate_repository_path,
instance_root_path=self.instance_root
)
# First bang
event = watchdog.process_state_events[0]
instance.sequence = []
instance.header_list = []
headers = {'eventname': event}
payload = 'processname:%s groupname:%s from_state:RUNNING' % (
'daemon' + WATCHDOG_MARK, instance.name)
watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, ['/softwareInstanceBang'])
# Second bang
event = watchdog.process_state_events[0]
instance.sequence = []
instance.header_list = []
headers = {'eventname': event}
payload = 'processname:%s groupname:%s from_state:RUNNING' % (
'daemon' + WATCHDOG_MARK, instance.name)
watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, [])
def test_watchdog_bang_only_once_if_timestamp_did_not_change(self):
"""
For a partition that has been successfully deployed (promises are passing,
etc), test that:
* First bang is transmitted
* subsequent bangs are ignored until a new deployment is successful.
Scenario:
* slapgrid successfully deploys a partition
* A process crashes, watchdog calls bang
* Another deployment (run of slapgrid) is done, but not successful (
promise is failing)
* The process crashes again, but watchdog ignores it
* Yet another deployment is done, and it is successful
* The process crashes again, watchdog calls bang
* The process crashes again, watchdog ignroes it
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
certificate_repository_path = os.path.join(self._tempdir, 'partition_pki')
instance.setCertificate(certificate_repository_path)
partition = os.path.join(self.instance_root, '0')
timestamp_content = '1234'
timestamp_file = open(os.path.join(partition, slapos.grid.slapgrid.COMPUTER_PARTITION_TIMESTAMP_FILENAME), 'w')
timestamp_file.write(timestamp_content)
timestamp_file.close()
watchdog = Watchdog(
master_url='https://127.0.0.1/',
computer_id=self.computer_id,
certificate_repository_path=certificate_repository_path,
instance_root_path=self.instance_root
)
# First bang
event = watchdog.process_state_events[0]
instance.sequence = []
instance.header_list = []
headers = {'eventname': event}
payload = 'processname:%s groupname:%s from_state:RUNNING' % (
'daemon' + WATCHDOG_MARK, instance.name)
watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, ['/softwareInstanceBang'])
self.assertEqual(open(os.path.join(partition, slapos.grid.slapgrid.COMPUTER_PARTITION_LATEST_BANG_TIMESTAMP_FILENAME)).read(), timestamp_content)
# Second bang
event = watchdog.process_state_events[0]
instance.sequence = []
instance.header_list = []
headers = {'eventname': event}
payload = 'processname:%s groupname:%s from_state:RUNNING' % (
'daemon' + WATCHDOG_MARK, instance.name)
watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, [])
# Second successful deployment
timestamp_content = '12345'
timestamp_file = open(os.path.join(partition, slapos.grid.slapgrid.COMPUTER_PARTITION_TIMESTAMP_FILENAME), 'w')
timestamp_file.write(timestamp_content)
timestamp_file.close()
# Third bang
event = watchdog.process_state_events[0]
instance.sequence = []
instance.header_list = []
headers = {'eventname': event}
payload = 'processname:%s groupname:%s from_state:RUNNING' % (
'daemon' + WATCHDOG_MARK, instance.name)
watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, ['/softwareInstanceBang'])
self.assertEqual(open(os.path.join(partition, slapos.grid.slapgrid.COMPUTER_PARTITION_LATEST_BANG_TIMESTAMP_FILENAME)).read(), timestamp_content)
# Fourth bang
event = watchdog.process_state_events[0]
instance.sequence = []
instance.header_list = []
headers = {'eventname': event}
payload = 'processname:%s groupname:%s from_state:RUNNING' % (
'daemon' + WATCHDOG_MARK, instance.name)
watchdog.handle_event(headers, payload)
self.assertEqual(instance.sequence, [])
class TestSlapgridCPPartitionProcessing(MasterMixin, unittest.TestCase):
def test_partition_timestamp(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
timestamp = str(int(time.time()))
instance.timestamp = timestamp
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
partition = os.path.join(self.instance_root, '0')
self.assertItemsEqual(os.listdir(partition),
['.slapgrid', '.timestamp', 'buildout.cfg', 'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
timestamp_path = os.path.join(instance.partition_path, '.timestamp')
self.setSlapgrid()
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertIn(timestamp, open(timestamp_path).read())
self.assertEqual(instance.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
def test_partition_timestamp_develop(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
timestamp = str(int(time.time()))
instance.timestamp = timestamp
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
partition = os.path.join(self.instance_root, '0')
self.assertItemsEqual(os.listdir(partition),
['.slapgrid', '.timestamp', 'buildout.cfg',
'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(self.launchSlapgrid(develop=True),
slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(instance.sequence,
['/availableComputerPartition', '/stoppedComputerPartition',
'/availableComputerPartition', '/stoppedComputerPartition'])
def test_partition_old_timestamp(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
timestamp = str(int(time.time()))
instance.timestamp = timestamp
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
partition = os.path.join(self.instance_root, '0')
self.assertItemsEqual(os.listdir(partition),
['.slapgrid', '.timestamp', 'buildout.cfg', 'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
instance.timestamp = str(int(timestamp) - 1)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(instance.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
def test_partition_timestamp_new_timestamp(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
timestamp = str(int(time.time()))
instance.timestamp = timestamp
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
partition = os.path.join(self.instance_root, '0')
self.assertItemsEqual(os.listdir(partition),
['.slapgrid', '.timestamp', 'buildout.cfg', 'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
instance.timestamp = str(int(timestamp) + 1)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(self.launchSlapgrid(), slapgrid.SLAPGRID_SUCCESS)
self.assertEqual(computer.sequence,
['/getHateoasUrl',
'/getFullComputerInformation', '/availableComputerPartition',
'/stoppedComputerPartition',
'/getHateoasUrl', '/getFullComputerInformation',
'/availableComputerPartition', '/stoppedComputerPartition',
'/getHateoasUrl',
'/getFullComputerInformation'])
def test_partition_timestamp_no_timestamp(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
timestamp = str(int(time.time()))
instance.timestamp = timestamp
self.launchSlapgrid()
self.assertInstanceDirectoryListEqual(['0'])
partition = os.path.join(self.instance_root, '0')
self.assertItemsEqual(os.listdir(partition),
['.slapgrid', '.timestamp', 'buildout.cfg', 'software_release', 'worked', '.slapos-retention-lock-delay'])
self.assertItemsEqual(os.listdir(self.software_root),
[instance.software.software_hash])
instance.timestamp = None
self.launchSlapgrid()
self.assertEqual(computer.sequence,
['/getHateoasUrl',
'/getFullComputerInformation', '/availableComputerPartition',
'/stoppedComputerPartition',
'/getHateoasUrl', '/getFullComputerInformation',
'/availableComputerPartition', '/stoppedComputerPartition'])
def test_partition_periodicity_remove_timestamp(self):
"""
Check that if periodicity forces run of buildout for a partition, it
removes the .timestamp file.
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
timestamp = str(int(time.time()))
instance.timestamp = timestamp
instance.requested_state = 'started'
instance.software.setPeriodicity(1)
self.launchSlapgrid()
partition = os.path.join(self.instance_root, '0')
self.assertItemsEqual(os.listdir(partition),
['.slapgrid', '.timestamp', 'buildout.cfg',
'software_release', 'worked', '.slapos-retention-lock-delay'])
time.sleep(2)
# dummify install() so that it doesn't actually do anything so that it
# doesn't recreate .timestamp.
instance.install = lambda: None
self.launchSlapgrid()
self.assertItemsEqual(os.listdir(partition),
['.slapgrid', '.timestamp', 'buildout.cfg',
'software_release', 'worked', '.slapos-retention-lock-delay'])
def test_one_partition_periodicity_from_file_does_not_disturb_others(self):
"""
If time between last processing of instance and now is superior
to periodicity then instance should be proceed
1. We set a wanted maximum_periodicity in periodicity file in
in one software release directory and not the other one
2. We process computer partition and check if wanted_periodicity was
used as maximum_periodicty
3. We wait for a time superior to wanted_periodicty
4. We launch processComputerPartition and check that partition using
software with periodicity was runned and not the other
5. We check that modification time of .timestamp was modified
"""
computer = ComputerForTest(self.software_root, self.instance_root, 20, 20)
with httmock.HTTMock(computer.request_handler):
instance0 = computer.instance_list[0]
timestamp = str(int(time.time() - 5))
instance0.timestamp = timestamp
instance0.requested_state = 'started'
for instance in computer.instance_list[1:]:
instance.software = \
computer.software_list[computer.instance_list.index(instance)]
instance.timestamp = timestamp
wanted_periodicity = 1
instance0.software.setPeriodicity(wanted_periodicity)
self.launchSlapgrid()
self.assertNotEqual(wanted_periodicity, self.grid.maximum_periodicity)
last_runtime = os.path.getmtime(
os.path.join(instance0.partition_path, '.timestamp'))
time.sleep(wanted_periodicity + 1)
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
time.sleep(1)
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
['/availableComputerPartition', '/startedComputerPartition',
'/availableComputerPartition', '/startedComputerPartition',
])
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
self.assertGreater(
os.path.getmtime(os.path.join(instance0.partition_path, '.timestamp')),
last_runtime)
self.assertNotEqual(wanted_periodicity, self.grid.maximum_periodicity)
def test_one_partition_stopped_is_not_processed_after_periodicity(self):
"""
Check that periodicity forces processing a partition even if it is not
started.
"""
computer = ComputerForTest(self.software_root, self.instance_root, 20, 20)
with httmock.HTTMock(computer.request_handler):
instance0 = computer.instance_list[0]
timestamp = str(int(time.time() - 5))
instance0.timestamp = timestamp
for instance in computer.instance_list[1:]:
instance.software = \
computer.software_list[computer.instance_list.index(instance)]
instance.timestamp = timestamp
wanted_periodicity = 1
instance0.software.setPeriodicity(wanted_periodicity)
self.launchSlapgrid()
self.assertNotEqual(wanted_periodicity, self.grid.maximum_periodicity)
last_runtime = os.path.getmtime(
os.path.join(instance0.partition_path, '.timestamp'))
time.sleep(wanted_periodicity + 1)
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
time.sleep(1)
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
['/availableComputerPartition', '/stoppedComputerPartition',
'/availableComputerPartition', '/stoppedComputerPartition'])
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
self.assertNotEqual(os.path.getmtime(os.path.join(instance0.partition_path,
'.timestamp')),
last_runtime)
self.assertNotEqual(wanted_periodicity, self.grid.maximum_periodicity)
def test_one_partition_destroyed_is_not_processed_after_periodicity(self):
"""
Check that periodicity forces processing a partition even if it is not
started.
"""
computer = ComputerForTest(self.software_root, self.instance_root, 20, 20)
with httmock.HTTMock(computer.request_handler):
instance0 = computer.instance_list[0]
timestamp = str(int(time.time() - 5))
instance0.timestamp = timestamp
instance0.requested_state = 'stopped'
for instance in computer.instance_list[1:]:
instance.software = \
computer.software_list[computer.instance_list.index(instance)]
instance.timestamp = timestamp
wanted_periodicity = 1
instance0.software.setPeriodicity(wanted_periodicity)
self.launchSlapgrid()
self.assertNotEqual(wanted_periodicity, self.grid.maximum_periodicity)
last_runtime = os.path.getmtime(
os.path.join(instance0.partition_path, '.timestamp'))
time.sleep(wanted_periodicity + 1)
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
time.sleep(1)
instance0.requested_state = 'destroyed'
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
['/availableComputerPartition', '/stoppedComputerPartition',
'/stoppedComputerPartition'])
for instance in computer.instance_list[1:]:
self.assertEqual(instance.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
self.assertNotEqual(os.path.getmtime(os.path.join(instance0.partition_path,
'.timestamp')),
last_runtime)
self.assertNotEqual(wanted_periodicity, self.grid.maximum_periodicity)
def test_one_partition_is_never_processed_when_periodicity_is_negative(self):
"""
Checks that a partition is not processed when
its periodicity is negative
1. We setup one instance and set periodicity at -1
2. We mock the install method from slapos.grid.slapgrid.Partition
3. We launch slapgrid once so that .timestamp file is created and check that install method is
indeed called (through mocked_method.called
4. We launch slapgrid anew and check that install as not been called again
"""
computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
with httmock.HTTMock(computer.request_handler):
timestamp = str(int(time.time()))
instance = computer.instance_list[0]
instance.software.setPeriodicity(-1)
instance.timestamp = timestamp
with patch.object(slapos.grid.slapgrid.Partition, 'install', return_value=None) as mock_method:
self.launchSlapgrid()
self.assertTrue(mock_method.called)
self.launchSlapgrid()
self.assertEqual(mock_method.call_count, 1)
def test_one_partition_is_always_processed_when_periodicity_is_zero(self):
"""
Checks that a partition is always processed when
its periodicity is 0
1. We setup one instance and set periodicity at 0
2. We mock the install method from slapos.grid.slapgrid.Partition
3. We launch slapgrid once so that .timestamp file is created
4. We launch slapgrid anew and check that install has been called twice (one time because of the
new setup and one time because of periodicity = 0)
"""
computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
with httmock.HTTMock(computer.request_handler):
timestamp = str(int(time.time()))
instance = computer.instance_list[0]
instance.software.setPeriodicity(0)
instance.timestamp = timestamp
with patch.object(slapos.grid.slapgrid.Partition, 'install', return_value=None) as mock_method:
self.launchSlapgrid()
self.launchSlapgrid()
self.assertEqual(mock_method.call_count, 2)
def test_one_partition_buildout_fail_does_not_disturb_others(self):
"""
1. We set up two instance one using a corrupted buildout
2. One will fail but the other one will be processed correctly
"""
computer = ComputerForTest(self.software_root, self.instance_root, 2, 2)
with httmock.HTTMock(computer.request_handler):
instance0 = computer.instance_list[0]
instance1 = computer.instance_list[1]
instance1.software = computer.software_list[1]
instance0.software.setBuildout("""#!/bin/sh
exit 42""")
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
['/softwareInstanceError'])
self.assertEqual(instance1.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
def test_one_partition_lacking_software_path_does_not_disturb_others(self):
"""
1. We set up two instance but remove software path of one
2. One will fail but the other one will be processed correctly
"""
computer = ComputerForTest(self.software_root, self.instance_root, 2, 2)
with httmock.HTTMock(computer.request_handler):
instance0 = computer.instance_list[0]
instance1 = computer.instance_list[1]
instance1.software = computer.software_list[1]
shutil.rmtree(instance0.software.srdir)
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
['/softwareInstanceError'])
self.assertEqual(instance1.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
def test_one_partition_lacking_software_bin_path_does_not_disturb_others(self):
"""
1. We set up two instance but remove software bin path of one
2. One will fail but the other one will be processed correctly
"""
computer = ComputerForTest(self.software_root, self.instance_root, 2, 2)
with httmock.HTTMock(computer.request_handler):
instance0 = computer.instance_list[0]
instance1 = computer.instance_list[1]
instance1.software = computer.software_list[1]
shutil.rmtree(instance0.software.srbindir)
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
['/softwareInstanceError'])
self.assertEqual(instance1.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
def test_one_partition_lacking_path_does_not_disturb_others(self):
"""
1. We set up two instances but remove path of one
2. One will fail but the other one will be processed correctly
"""
computer = ComputerForTest(self.software_root, self.instance_root, 2, 2)
with httmock.HTTMock(computer.request_handler):
instance0 = computer.instance_list[0]
instance1 = computer.instance_list[1]
instance1.software = computer.software_list[1]
shutil.rmtree(instance0.partition_path)
self.launchSlapgrid()
self.assertEqual(instance0.sequence,
['/softwareInstanceError'])
self.assertEqual(instance1.sequence,
['/availableComputerPartition', '/stoppedComputerPartition'])
def test_one_partition_buildout_fail_is_correctly_logged(self):
"""
1. We set up an instance using a corrupted buildout
2. It will fail, make sure that whole log is sent to master
"""
computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
line1 = "Nerdy kitten: Can I haz a process crash?"
line2 = "Cedric: Sure, here it is."
instance.software.setBuildout("""#!/bin/sh
echo %s; echo %s; exit 42""" % (line1, line2))
self.launchSlapgrid()
self.assertEqual(instance.sequence, ['/softwareInstanceError'])
# We don't care of actual formatting, we just want to have full log
self.assertIn(line1, instance.error_log)
self.assertIn(line2, instance.error_log)
self.assertIn('Failed to run buildout', instance.error_log)
class TestSlapgridUsageReport(MasterMixin, unittest.TestCase):
"""
Test suite about slapgrid-ur
"""
def test_slapgrid_destroys_instance_to_be_destroyed(self):
"""
Test than an instance in "destroyed" state is correctly destroyed
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
instance.software.setBuildout(WRAPPER_CONTENT)
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertLogContent(wrapper_log, 'Working')
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/availableComputerPartition',
'/startedComputerPartition'])
self.assertEqual(instance.state, 'started')
# Then destroy the instance
computer.sequence = []
instance.requested_state = 'destroyed'
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path), [])
self.assertItemsEqual(os.listdir(self.software_root),
[instance.software.software_hash])
# Assert supervisor stopped process
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertIsNotCreated(wrapper_log)
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/stoppedComputerPartition',
'/destroyedComputerPartition'])
self.assertEqual(instance.state, 'destroyed')
def test_partition_list_is_complete_if_empty_destroyed_partition(self):
"""
Test that an empty partition with destroyed state but with SR informations
Is correctly destroyed
Axiom: each valid partition has a state and a software_release.
Scenario:
1. Simulate computer containing one "destroyed" partition but with valid SR
2. See if it destroyed
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
computer.sequence = []
instance.requested_state = 'destroyed'
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path), [])
self.assertItemsEqual(os.listdir(self.software_root),
[instance.software.software_hash])
# Assert supervisor stopped process
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertIsNotCreated(wrapper_log)
self.assertEqual(
computer.sequence,
['/getFullComputerInformation', '/stoppedComputerPartition', '/destroyedComputerPartition'])
def test_slapgrid_not_destroy_bad_instance(self):
"""
Checks that slapgrid-ur don't destroy instance not to be destroyed.
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
instance.software.setBuildout(WRAPPER_CONTENT)
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertLogContent(wrapper_log, 'Working')
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence,
['/getFullComputerInformation',
'/availableComputerPartition',
'/startedComputerPartition'])
self.assertEqual('started', instance.state)
# Then run usage report and see if it is still working
computer.sequence = []
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertLogContent(wrapper_log, 'Working')
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path),
['.slapgrid', '.0_wrapper.log', 'buildout.cfg',
'etc', 'software_release', 'worked', '.slapos-retention-lock-delay'])
wrapper_log = os.path.join(instance.partition_path, '.0_wrapper.log')
self.assertLogContent(wrapper_log, 'Working')
self.assertEqual(computer.sequence,
['/getFullComputerInformation'])
self.assertEqual('started', instance.state)
def test_slapgrid_instance_ignore_free_instance(self):
"""
Test than a free instance (so in "destroyed" state, but empty, without
software_release URI) is ignored by slapgrid-cp.
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.software.name = None
computer.sequence = []
instance.requested_state = 'destroyed'
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path), [])
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence, ['/getFullComputerInformation'])
def test_slapgrid_report_ignore_free_instance(self):
"""
Test than a free instance (so in "destroyed" state, but empty, without
software_release URI) is ignored by slapgrid-ur.
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.software.name = None
computer.sequence = []
instance.requested_state = 'destroyed'
self.assertEqual(self.grid.agregateAndSendUsage(), slapgrid.SLAPGRID_SUCCESS)
# Assert partition directory is empty
self.assertInstanceDirectoryListEqual(['0'])
self.assertItemsEqual(os.listdir(instance.partition_path), [])
self.assertItemsEqual(os.listdir(self.software_root), [instance.software.software_hash])
self.assertEqual(computer.sequence, ['/getFullComputerInformation'])
class TestSlapgridSoftwareRelease(MasterMixin, unittest.TestCase):
def test_one_software_buildout_fail_is_correctly_logged(self):
"""
1. We set up a software using a corrupted buildout
2. It will fail, make sure that whole log is sent to master
"""
computer = ComputerForTest(self.software_root, self.instance_root, 1, 1)
with httmock.HTTMock(computer.request_handler):
software = computer.software_list[0]
line1 = "Nerdy kitten: Can I haz a process crash?"
line2 = "Cedric: Sure, here it is."
software.setBuildout("""#!/bin/sh
echo %s; echo %s; exit 42""" % (line1, line2))
self.launchSlapgridSoftware()
self.assertEqual(software.sequence,
['/buildingSoftwareRelease', '/softwareReleaseError'])
# We don't care of actual formatting, we just want to have full log
self.assertIn(line1, software.error_log)
self.assertIn(line2, software.error_log)
self.assertIn('Failed to run buildout', software.error_log)
class SlapgridInitialization(unittest.TestCase):
"""
"Abstract" class setting setup and teardown for TestSlapgridArgumentTuple
and TestSlapgridConfigurationFile.
"""
def setUp(self):
"""
Create the minimun default argument and configuration.
"""
self.certificate_repository_path = tempfile.mkdtemp()
self.fake_file_descriptor = tempfile.NamedTemporaryFile()
self.slapos_config_descriptor = tempfile.NamedTemporaryFile()
self.slapos_config_descriptor.write("""
[slapos]
software_root = /opt/slapgrid
instance_root = /srv/slapgrid
master_url = https://slap.vifib.com/
computer_id = your computer id
buildout = /path/to/buildout/binary
""")
self.slapos_config_descriptor.seek(0)
self.default_arg_tuple = (
'--cert_file', self.fake_file_descriptor.name,
'--key_file', self.fake_file_descriptor.name,
'--master_ca_file', self.fake_file_descriptor.name,
'--certificate_repository_path', self.certificate_repository_path,
'-c', self.slapos_config_descriptor.name, '--now')
self.signature_key_file_descriptor = tempfile.NamedTemporaryFile()
self.signature_key_file_descriptor.seek(0)
def tearDown(self):
"""
Removing the temp file.
"""
self.fake_file_descriptor.close()
self.slapos_config_descriptor.close()
self.signature_key_file_descriptor.close()
shutil.rmtree(self.certificate_repository_path, True)
class TestSlapgridCPWithMasterPromise(MasterMixin, unittest.TestCase):
def test_one_failing_promise(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
worked_file = os.path.join(instance.partition_path, 'fail_worked')
fail = textwrap.dedent("""\
#!/usr/bin/env sh
touch "%s"
exit 127""" % worked_file)
instance.setPromise('fail', fail)
self.assertEqual(self.grid.processComputerPartitionList(),
slapos.grid.slapgrid.SLAPGRID_PROMISE_FAIL)
self.assertTrue(os.path.isfile(worked_file))
self.assertTrue(instance.error)
self.assertNotEqual('started', instance.state)
def test_one_succeeding_promise(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
self.fake_waiting_time = 0.1
worked_file = os.path.join(instance.partition_path, 'succeed_worked')
succeed = textwrap.dedent("""\
#!/usr/bin/env sh
touch "%s"
exit 0""" % worked_file)
instance.setPromise('succeed', succeed)
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
self.assertTrue(os.path.isfile(worked_file))
self.assertFalse(instance.error)
self.assertEqual(instance.state, 'started')
def test_stderr_has_been_sent(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
self.fake_waiting_time = 0.5
promise_path = os.path.join(instance.partition_path, 'etc', 'promise')
os.makedirs(promise_path)
succeed = os.path.join(promise_path, 'stderr_writer')
worked_file = os.path.join(instance.partition_path, 'stderr_worked')
with open(succeed, 'w') as f:
f.write(textwrap.dedent("""\
#!/usr/bin/env sh
touch "%s"
echo Error 1>&2
exit 127""" % worked_file))
os.chmod(succeed, 0o777)
self.assertEqual(self.grid.processComputerPartitionList(),
slapos.grid.slapgrid.SLAPGRID_PROMISE_FAIL)
self.assertTrue(os.path.isfile(worked_file))
self.assertEqual(instance.error_log[-5:], 'Error')
self.assertTrue(instance.error)
self.assertIsNone(instance.state)
def test_timeout_works(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
self.fake_waiting_time = 0.1
promise_path = os.path.join(instance.partition_path, 'etc', 'promise')
os.makedirs(promise_path)
succeed = os.path.join(promise_path, 'timed_out_promise')
worked_file = os.path.join(instance.partition_path, 'timed_out_worked')
with open(succeed, 'w') as f:
f.write(textwrap.dedent("""\
#!/usr/bin/env sh
touch "%s"
sleep 5
exit 0""" % worked_file))
os.chmod(succeed, 0o777)
self.assertEqual(self.grid.processComputerPartitionList(),
slapos.grid.slapgrid.SLAPGRID_PROMISE_FAIL)
self.assertTrue(os.path.isfile(worked_file))
self.assertTrue(instance.error)
self.assertIsNone(instance.state)
def test_two_succeeding_promises(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
self.fake_waiting_time = 0.1
for i in range(2):
worked_file = os.path.join(instance.partition_path, 'succeed_%s_worked' % i)
succeed = textwrap.dedent("""\
#!/usr/bin/env sh
touch "%s"
exit 0""" % worked_file)
instance.setPromise('succeed_%s' % i, succeed)
self.assertEqual(self.grid.processComputerPartitionList(), slapgrid.SLAPGRID_SUCCESS)
for i in range(2):
worked_file = os.path.join(instance.partition_path, 'succeed_%s_worked' % i)
self.assertTrue(os.path.isfile(worked_file))
self.assertFalse(instance.error)
self.assertEqual(instance.state, 'started')
def test_one_succeeding_one_failing_promises(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
self.fake_waiting_time = 0.1
for i in range(2):
worked_file = os.path.join(instance.partition_path, 'promise_worked_%d' % i)
lockfile = os.path.join(instance.partition_path, 'lock')
promise = textwrap.dedent("""\
#!/usr/bin/env sh
touch "%(worked_file)s"
if [ ! -f %(lockfile)s ]
then
touch "%(lockfile)s"
exit 0
else
exit 127
fi""" % {
'worked_file': worked_file,
'lockfile': lockfile
})
instance.setPromise('promise_%s' % i, promise)
self.assertEqual(self.grid.processComputerPartitionList(),
slapos.grid.slapgrid.SLAPGRID_PROMISE_FAIL)
self.assertEquals(instance.error, 1)
self.assertNotEqual('started', instance.state)
def test_one_succeeding_one_timing_out_promises(self):
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
self.fake_waiting_time = 0.1
for i in range(2):
worked_file = os.path.join(instance.partition_path, 'promise_worked_%d' % i)
lockfile = os.path.join(instance.partition_path, 'lock')
promise = textwrap.dedent("""\
#!/usr/bin/env sh
touch "%(worked_file)s"
if [ ! -f %(lockfile)s ]
then
touch "%(lockfile)s"
else
sleep 5
fi
exit 0""" % {
'worked_file': worked_file,
'lockfile': lockfile}
)
instance.setPromise('promise_%d' % i, promise)
self.assertEqual(self.grid.processComputerPartitionList(),
slapos.grid.slapgrid.SLAPGRID_PROMISE_FAIL)
self.assertEquals(instance.error, 1)
self.assertNotEqual(instance.state, 'started')
class TestSlapgridDestructionLock(MasterMixin, unittest.TestCase):
def test_retention_lock(self):
"""
Higher level test about actual retention (or no-retention) of instance
if specifying a retention lock delay.
"""
computer = ComputerForTest(self.software_root, self.instance_root)
with httmock.HTTMock(computer.request_handler):
instance = computer.instance_list[0]
instance.requested_state = 'started'
instance.filter_dict = {'retention_delay': 1.0 / (3600 * 24)}
self.grid.processComputerPartitionList()
dummy_instance_file_path = os.path.join(instance.partition_path, 'dummy')
with open(dummy_instance_file_path, 'w') as dummy_instance_file:
dummy_instance_file.write('dummy')
self.assertTrue(os.path.exists(os.path.join(
instance.partition_path,
slapos.grid.SlapObject.Partition.retention_lock_delay_filename
)))
instance.requested_state = 'destroyed'
self.grid.agregateAndSendUsage()
self.assertTrue(os.path.exists(dummy_instance_file_path))
self.assertTrue(os.path.exists(os.path.join(
instance.partition_path,
slapos.grid.SlapObject.Partition.retention_lock_date_filename
)))
self.grid.agregateAndSendUsage()
self.assertTrue(os.path.exists(dummy_instance_file_path))
time.sleep(1)
self.grid.agregateAndSendUsage()
self.assertFalse(os.path.exists(dummy_instance_file_path))
slapos.core-1.3.10/slapos/tests/slapmock/ 0000755 0000000 0000000 00000000000 12517721227 017004 5 ustar root root slapos.core-1.3.10/slapos/tests/slapmock/__init__.py 0000644 0000000 0000000 00000000000 12517720512 021077 0 ustar root root slapos.core-1.3.10/slapos/tests/slapmock/requests.py 0000644 0000000 0000000 00000000216 12517720512 021224 0 ustar root root # -*- coding: utf-8 -*-
def response_ok(url, request):
return {
'status_code': 200,
'content': ''
}
slapos.core-1.3.10/slapos/tests/interface.py 0000644 0000000 0000000 00000010060 12517720512 017476 0 ustar root root ##############################################################################
#
# Copyright (c) 2010 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import unittest
from zope.interface.verify import verifyClass
import zope.interface
import types
from slapos import slap
def getOnlyImplementationAssertionMethod(klass, method_list):
"""Returns method which verifies if a klass only implements its interfaces"""
def testMethod(self):
implemented_method_list = [x for x in dir(klass) \
if ((not x.startswith('_')) and callable(getattr(klass, x)))]
for interface_method in method_list:
if interface_method in implemented_method_list:
implemented_method_list.remove(interface_method)
if implemented_method_list:
raise AssertionError("Unexpected methods %s" % implemented_method_list)
return testMethod
def getImplementationAssertionMethod(klass, interface):
"""Returns method which verifies if interface is properly implemented by klass"""
def testMethod(self):
verifyClass(interface, klass)
return testMethod
def getDeclarationAssertionMethod(klass):
"""Returns method which verifies if klass is declaring interface"""
def testMethod(self):
if len(list(zope.interface.implementedBy(klass))) == 0:
self.fail('%s class does not respect its interface(s).' % klass.__name__)
return testMethod
def generateTestMethodListOnClass(klass, module):
"""Generate test method on klass"""
for class_id in dir(module):
implementing_class = getattr(module, class_id)
if type(implementing_class) not in (types.ClassType, types.TypeType):
continue
# add methods to assert that publicly available classes are defining
# interfaces
method_name = 'test_%s_declares_interface' % (class_id,)
setattr(klass, method_name, getDeclarationAssertionMethod(
implementing_class))
implemented_method_list = []
for interface in list(zope.interface.implementedBy(implementing_class)):
# for each interface which class declares add a method which verify
# implementation
method_name = 'test_%s_implements_%s' % (class_id,
interface.__identifier__)
setattr(klass, method_name, getImplementationAssertionMethod(
implementing_class, interface))
for interface_klass in interface.__iro__:
implemented_method_list.extend(interface_klass.names())
# for each interface which class declares, check that no other method are
# available
method_name = 'test_%s_only_implements' % class_id
setattr(klass, method_name, getOnlyImplementationAssertionMethod(
implementing_class,
implemented_method_list))
class TestInterface(unittest.TestCase):
"""Tests all publicly available classes of slap
Classes are checked *if* they implement interface and if the implementation
is correct.
"""
# add methods to test class
generateTestMethodListOnClass(TestInterface, slap)
if __name__ == '__main__':
unittest.main()
slapos.core-1.3.10/slapos/tests/slapformat.py 0000644 0000000 0000000 00000060542 12517720512 017720 0 ustar root root # -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2012 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import logging
import slapos.format
import slapos.util
import unittest
import netaddr
import socket
# for mocking
import grp
import netifaces
import os
import pwd
import time
USER_LIST = []
GROUP_LIST = []
INTERFACE_DICT = {}
class FakeConfig:
pass
class TestLoggerHandler(logging.Handler):
def __init__(self, *args, **kwargs):
self.bucket = []
logging.Handler.__init__(self, *args, **kwargs)
def emit(self, record):
self.bucket.append(record.msg)
class FakeCallAndRead:
def __init__(self):
self.external_command_list = []
def __call__(self, argument_list, raise_on_error=True):
retval = 0, 'UP'
global INTERFACE_DICT
if 'useradd' in argument_list:
print argument_list
global USER_LIST
username = argument_list[-1]
if username == '-r':
username = argument_list[-2]
USER_LIST.append(username)
elif 'groupadd' in argument_list:
global GROUP_LIST
GROUP_LIST.append(argument_list[-1])
elif argument_list[:3] == ['ip', 'addr', 'add']:
ip, interface = argument_list[3], argument_list[5]
if ':' not in ip:
netmask = netaddr.strategy.ipv4.int_to_str(
netaddr.strategy.ipv4.prefix_to_netmask[int(ip.split('/')[1])])
ip = ip.split('/')[0]
INTERFACE_DICT[interface][socket.AF_INET].append({'addr': ip, 'netmask': netmask})
else:
netmask = netaddr.strategy.ipv6.int_to_str(
netaddr.strategy.ipv6.prefix_to_netmask[int(ip.split('/')[1])])
ip = ip.split('/')[0]
INTERFACE_DICT[interface][socket.AF_INET6].append({'addr': ip, 'netmask': netmask})
# stabilise by mangling ip to just ip string
argument_list[3] = 'ip/%s' % netmask
elif argument_list[:3] == ['ip', 'addr', 'list'] or \
argument_list[:4] == ['ip', '-6', 'addr', 'list']:
retval = 0, str(INTERFACE_DICT)
elif argument_list[:3] == ['ip', 'route', 'show']:
retval = 0, 'OK'
elif argument_list[:3] == ['route', 'add', '-host']:
retval = 0, 'OK'
self.external_command_list.append(' '.join(argument_list))
return retval
class LoggableWrapper:
def __init__(self, logger, name):
self.__logger = logger
self.__name = name
def __call__(self, *args, **kwargs):
arg_list = [repr(x) for x in args] + [
'%s=%r' % (x, y) for x, y in kwargs.iteritems()]
self.__logger.debug('%s(%s)' % (self.__name, ', '.join(arg_list)))
class TimeMock:
@classmethod
def sleep(self, seconds):
return
class GrpMock:
@classmethod
def getgrnam(self, name):
global GROUP_LIST
if name in GROUP_LIST:
return True
raise KeyError
class PwdMock:
@classmethod
def getpwnam(self, name):
global USER_LIST
if name in USER_LIST:
class result:
pw_uid = 0
pw_gid = 0
return result
raise KeyError
class NetifacesMock:
@classmethod
def ifaddresses(self, name):
global INTERFACE_DICT
if name in INTERFACE_DICT:
return INTERFACE_DICT[name]
raise ValueError
@classmethod
def interfaces(self):
global INTERFACE_DICT
return INTERFACE_DICT.keys()
class SlaposUtilMock:
@classmethod
def chownDirectory(*args, **kw):
pass
class SlapformatMixin(unittest.TestCase):
# keep big diffs
maxDiff = None
def patchNetifaces(self):
self.netifaces = NetifacesMock()
self.saved_netifaces = {}
for fake in vars(NetifacesMock):
self.saved_netifaces[fake] = getattr(netifaces, fake, None)
setattr(netifaces, fake, getattr(self.netifaces, fake))
def restoreNetifaces(self):
for name, original_value in self.saved_netifaces.items():
setattr(netifaces, name, original_value)
del self.saved_netifaces
def patchPwd(self):
self.saved_pwd = {}
for fake in vars(PwdMock):
self.saved_pwd[fake] = getattr(pwd, fake, None)
setattr(pwd, fake, getattr(PwdMock, fake))
def restorePwd(self):
for name, original_value in self.saved_pwd.items():
setattr(pwd, name, original_value)
del self.saved_pwd
def patchTime(self):
self.saved_time = {}
for fake in vars(TimeMock):
self.saved_time[fake] = getattr(time, fake, None)
setattr(time, fake, getattr(TimeMock, fake))
def restoreTime(self):
for name, original_value in self.saved_time.items():
setattr(time, name, original_value)
del self.saved_time
def patchGrp(self):
self.saved_grp = {}
for fake in vars(GrpMock):
self.saved_grp[fake] = getattr(grp, fake, None)
setattr(grp, fake, getattr(GrpMock, fake))
def restoreGrp(self):
for name, original_value in self.saved_grp.items():
setattr(grp, name, original_value)
del self.saved_grp
def patchOs(self, logger):
self.saved_os = {}
for fake in ['mkdir', 'chown', 'chmod', 'makedirs']:
self.saved_os[fake] = getattr(os, fake, None)
f = LoggableWrapper(logger, fake)
setattr(os, fake, f)
def restoreOs(self):
for name, original_value in self.saved_os.items():
setattr(os, name, original_value)
del self.saved_os
def patchSlaposUtil(self):
self.saved_slapos_util = {}
for fake in ['chownDirectory']:
self.saved_slapos_util[fake] = getattr(slapos.util, fake, None)
setattr(slapos.util, fake, getattr(SlaposUtilMock, fake))
def restoreSlaposUtil(self):
for name, original_value in self.saved_slapos_util.items():
setattr(slapos.util, name, original_value)
del self.saved_slapos_util
def setUp(self):
config = FakeConfig()
config.dry_run = True
config.verbose = True
logger = logging.getLogger('testcatch')
logger.setLevel(logging.DEBUG)
self.test_result = TestLoggerHandler()
logger.addHandler(self.test_result)
config.logger = logger
self.partition = slapos.format.Partition('partition', '/part_path',
slapos.format.User('testuser'), [], None)
global USER_LIST
USER_LIST = []
global GROUP_LIST
GROUP_LIST = []
global INTERFACE_DICT
INTERFACE_DICT = {}
self.real_callAndRead = slapos.format.callAndRead
self.fakeCallAndRead = FakeCallAndRead()
slapos.format.callAndRead = self.fakeCallAndRead
self.patchOs(logger)
self.patchGrp()
self.patchTime()
self.patchPwd()
self.patchNetifaces()
self.patchSlaposUtil()
def tearDown(self):
self.restoreOs()
self.restoreGrp()
self.restoreTime()
self.restorePwd()
self.restoreNetifaces()
self.restoreSlaposUtil()
slapos.format.callAndRead = self.real_callAndRead
class TestComputer(SlapformatMixin):
def test_getAddress_empty_computer(self):
computer = slapos.format.Computer('computer')
self.assertEqual(computer.getAddress(), {'netmask': None, 'addr': None})
@unittest.skip("Not implemented")
def test_construct_empty(self):
computer = slapos.format.Computer('computer')
computer.construct()
@unittest.skip("Not implemented")
def test_construct_empty_prepared(self):
computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result,
name='bridge',
ipv4_local_network='127.0.0.1/16'))
computer.instance_root = '/instance_root'
computer.software_root = '/software_root'
computer.construct()
self.assertEqual([
"makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)",
"chown('/software_root', 0, 0)",
"chmod('/software_root', 493)"],
self.test_result.bucket)
self.assertEqual([
'ip addr list bridge',
'groupadd slapsoft',
'useradd -d /software_root -g slapsoft slapsoft -r'
],
self.fakeCallAndRead.external_command_list)
def test_construct_empty_prepared_no_alter_user(self):
computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result,
name='bridge',
ipv4_local_network='127.0.0.1/16'))
computer.instance_root = '/instance_root'
computer.software_root = '/software_root'
computer.construct(alter_user=False)
self.assertEqual([
"makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)",
"chmod('/software_root', 493)"],
self.test_result.bucket)
self.assertEqual(['ip addr list bridge'],
self.fakeCallAndRead.external_command_list)
@unittest.skip("Not implemented")
def test_construct_empty_prepared_no_alter_network(self):
computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result,
name='bridge',
ipv4_local_network='127.0.0.1/16'))
computer.instance_root = '/instance_root'
computer.software_root = '/software_root'
computer.construct(alter_network=False)
self.assertEqual([
"makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)",
"chown('/software_root', 0, 0)",
"chmod('/software_root', 493)"],
self.test_result.bucket)
self.assertEqual([
'ip addr list bridge',
'groupadd slapsoft',
'useradd -d /software_root -g slapsoft slapsoft -r'
],
self.fakeCallAndRead.external_command_list)
def test_construct_empty_prepared_no_alter_network_user(self):
computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result,
name='bridge',
ipv4_local_network='127.0.0.1/16'))
computer.instance_root = '/instance_root'
computer.software_root = '/software_root'
computer.construct(alter_network=False, alter_user=False)
self.assertEqual([
"makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)",
"chmod('/software_root', 493)"],
self.test_result.bucket)
self.assertEqual([
'ip addr list bridge',
],
self.fakeCallAndRead.external_command_list)
@unittest.skip("Not implemented")
def test_construct_prepared(self):
computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result,
name='bridge',
ipv4_local_network='127.0.0.1/16'))
computer.instance_root = '/instance_root'
computer.software_root = '/software_root'
partition = slapos.format.Partition('partition', '/part_path',
slapos.format.User('testuser'), [], None)
partition.tap = slapos.format.Tap('tap')
computer.partition_list = [partition]
global INTERFACE_DICT
INTERFACE_DICT['bridge'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
'netmask': '255.255.255.0'}],
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
}
computer.construct()
self.assertEqual([
"makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)",
"chown('/software_root', 0, 0)",
"chmod('/software_root', 493)",
"mkdir('/instance_root/partition', 488)",
"chown('/instance_root/partition', 0, 0)",
"chmod('/instance_root/partition', 488)"
],
self.test_result.bucket)
self.assertEqual([
'ip addr list bridge',
'groupadd slapsoft',
'useradd -d /software_root -g slapsoft slapsoft -r',
'groupadd testuser',
'useradd -d /instance_root/partition -g testuser -G slapsoft testuser -r',
'tunctl -t tap -u testuser',
'ip link set tap up',
'brctl show',
'brctl addif bridge tap',
'ip addr add ip/255.255.255.255 dev bridge',
'ip addr list bridge',
'ip addr add ip/ffff:ffff:ffff:ffff:: dev bridge',
'ip addr list bridge',
],
self.fakeCallAndRead.external_command_list)
def test_construct_prepared_no_alter_user(self):
computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result,
name='bridge',
ipv4_local_network='127.0.0.1/16'))
computer.instance_root = '/instance_root'
computer.software_root = '/software_root'
partition = slapos.format.Partition('partition', '/part_path',
slapos.format.User('testuser'), [], None)
global USER_LIST
USER_LIST = ['testuser']
partition.tap = slapos.format.Tap('tap')
computer.partition_list = [partition]
global INTERFACE_DICT
INTERFACE_DICT['bridge'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
'netmask': '255.255.255.0'}],
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
}
computer.construct(alter_user=False)
self.assertEqual([
"makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)",
"chmod('/software_root', 493)",
"mkdir('/instance_root/partition', 488)",
"chmod('/instance_root/partition', 488)"
],
self.test_result.bucket)
self.assertEqual([
'ip addr list bridge',
'tunctl -t tap -u testuser',
'ip link set tap up',
'brctl show',
'brctl addif bridge tap',
'ip addr add ip/255.255.255.255 dev bridge',
# 'ip addr list bridge',
'ip addr add ip/ffff:ffff:ffff:ffff:: dev bridge',
'ip -6 addr list bridge',
],
self.fakeCallAndRead.external_command_list)
def test_construct_prepared_tap_no_alter_user(self):
computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result,
name='iface',
ipv4_local_network='127.0.0.1/16'),
tap_gateway_interface='eth1')
computer.instance_root = '/instance_root'
computer.software_root = '/software_root'
partition = slapos.format.Partition('partition', '/part_path',
slapos.format.User('testuser'), [], None)
global USER_LIST
USER_LIST = ['testuser']
partition.tap = slapos.format.Tap('tap')
computer.partition_list = [partition]
global INTERFACE_DICT
INTERFACE_DICT['iface'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
'netmask': '255.255.255.0'}],
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
}
INTERFACE_DICT['eth1'] = {
socket.AF_INET: [{'addr': '10.8.0.1', 'broadcast': '10.8.0.254',
'netmask': '255.255.255.0'}]
}
computer.construct(alter_user=False)
self.assertEqual([
"makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)",
"chmod('/software_root', 493)",
"mkdir('/instance_root/partition', 488)",
"chmod('/instance_root/partition', 488)"
],
self.test_result.bucket)
self.assertEqual([
'ip addr list iface',
'tunctl -t tap -u testuser',
'ip link set tap up',
'ip route show 10.8.0.2',
'route add -host 10.8.0.2 dev tap',
'ip addr add ip/255.255.255.255 dev iface',
'ip addr add ip/ffff:ffff:ffff:ffff:: dev iface',
'ip -6 addr list iface'
],
self.fakeCallAndRead.external_command_list)
self.assertEqual(partition.tap.ipv4_addr, '10.8.0.2')
self.assertEqual(partition.tap.ipv4_netmask, '255.255.255.0')
self.assertEqual(partition.tap.ipv4_gateway, '10.8.0.1')
self.assertEqual(partition.tap.ipv4_network, '10.8.0.0')
@unittest.skip("Not implemented")
def test_construct_prepared_no_alter_network(self):
computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result,
name='bridge',
ipv4_local_network='127.0.0.1/16'))
computer.instance_root = '/instance_root'
computer.software_root = '/software_root'
partition = slapos.format.Partition('partition', '/part_path',
slapos.format.User('testuser'), [], None)
partition.tap = slapos.format.Tap('tap')
computer.partition_list = [partition]
global INTERFACE_DICT
INTERFACE_DICT['bridge'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
'netmask': '255.255.255.0'}],
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
}
computer.construct(alter_network=False)
self.assertEqual([
"makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)",
"chown('/software_root', 0, 0)",
"chmod('/software_root', 493)",
"mkdir('/instance_root/partition', 488)",
"chown('/instance_root/partition', 0, 0)",
"chmod('/instance_root/partition', 488)"
],
self.test_result.bucket)
self.assertEqual([
# 'ip addr list bridge',
'groupadd slapsoft',
'useradd -d /software_root -g slapsoft slapsoft -r',
'groupadd testuser',
'useradd -d /instance_root/partition -g testuser -G slapsoft testuser -r',
# 'ip addr add ip/255.255.255.255 dev bridge',
# 'ip addr list bridge',
# 'ip addr add ip/ffff:ffff:ffff:ffff:: dev bridge',
# 'ip addr list bridge',
],
self.fakeCallAndRead.external_command_list)
def test_construct_prepared_no_alter_network_user(self):
computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result,
name='bridge',
ipv4_local_network='127.0.0.1/16'))
computer.instance_root = '/instance_root'
computer.software_root = '/software_root'
partition = slapos.format.Partition('partition', '/part_path',
slapos.format.User('testuser'), [], None)
partition.tap = slapos.format.Tap('tap')
computer.partition_list = [partition]
global INTERFACE_DICT
INTERFACE_DICT['bridge'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
'netmask': '255.255.255.0'}],
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
}
computer.construct(alter_network=False, alter_user=False)
self.assertEqual([
"makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)",
"chmod('/software_root', 493)",
"mkdir('/instance_root/partition', 488)",
"chmod('/instance_root/partition', 488)"
],
self.test_result.bucket)
self.assertEqual([
'ip addr list bridge',
'ip addr add ip/255.255.255.255 dev bridge',
# 'ip addr list bridge',
'ip addr add ip/ffff:ffff:ffff:ffff:: dev bridge',
'ip -6 addr list bridge',
],
self.fakeCallAndRead.external_command_list)
def test_construct_use_unique_local_address_block(self):
"""
Test that slapformat creates a unique local address in the interface.
"""
global USER_LIST
USER_LIST = ['root']
computer = slapos.format.Computer('computer',
interface=slapos.format.Interface(logger=self.test_result,
name='myinterface',
ipv4_local_network='127.0.0.1/16'))
computer.instance_root = '/instance_root'
computer.software_root = '/software_root'
partition = slapos.format.Partition('partition', '/part_path',
slapos.format.User('testuser'), [], None)
partition.tap = slapos.format.Tap('tap')
computer.partition_list = [partition]
global INTERFACE_DICT
INTERFACE_DICT['myinterface'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
'netmask': '255.255.255.0'}],
socket.AF_INET6: [{'addr': '2a01:e35:2e27::e59c', 'netmask': 'ffff:ffff:ffff:ffff::'}]
}
computer.construct(use_unique_local_address_block=True, alter_user=False, create_tap=False)
self.assertEqual([
"makedirs('/instance_root', 493)",
"makedirs('/software_root', 493)",
"chmod('/software_root', 493)",
"mkdir('/instance_root/partition', 488)",
"chmod('/instance_root/partition', 488)"
],
self.test_result.bucket)
self.assertEqual([
'ip addr list myinterface',
'ip address add dev myinterface fd00::1/64',
'ip addr add ip/255.255.255.255 dev myinterface',
'ip addr add ip/ffff:ffff:ffff:ffff:: dev myinterface',
'ip -6 addr list myinterface'
],
self.fakeCallAndRead.external_command_list)
class TestPartition(SlapformatMixin):
def test_createPath_no_alter_user(self):
self.partition.createPath(False)
self.assertEqual(
[
"mkdir('/part_path', 488)",
"chmod('/part_path', 488)"
],
self.test_result.bucket
)
class TestUser(SlapformatMixin):
def test_create(self):
user = slapos.format.User('doesnotexistsyet')
user.setPath('/doesnotexistsyet')
user.create()
self.assertEqual([
'groupadd doesnotexistsyet',
'useradd -d /doesnotexistsyet -g doesnotexistsyet '\
'doesnotexistsyet -r'
],
self.fakeCallAndRead.external_command_list)
def test_create_additional_groups(self):
user = slapos.format.User('doesnotexistsyet', ['additionalgroup1',
'additionalgroup2'])
user.setPath('/doesnotexistsyet')
user.create()
self.assertEqual([
'groupadd doesnotexistsyet',
'useradd -d /doesnotexistsyet -g doesnotexistsyet -G '\
'additionalgroup1,additionalgroup2 doesnotexistsyet -r'
],
self.fakeCallAndRead.external_command_list)
def test_create_group_exists(self):
global GROUP_LIST
GROUP_LIST = ['testuser']
user = slapos.format.User('testuser')
user.setPath('/testuser')
user.create()
self.assertEqual([
'useradd -d /testuser -g testuser testuser -r'
],
self.fakeCallAndRead.external_command_list)
def test_create_user_exists_additional_groups(self):
global USER_LIST
USER_LIST = ['testuser']
user = slapos.format.User('testuser', ['additionalgroup1',
'additionalgroup2'])
user.setPath('/testuser')
user.create()
self.assertEqual([
'groupadd testuser',
'usermod -d /testuser -g testuser -G '\
'additionalgroup1,additionalgroup2 testuser'
],
self.fakeCallAndRead.external_command_list)
def test_create_user_exists(self):
global USER_LIST
USER_LIST = ['testuser']
user = slapos.format.User('testuser')
user.setPath('/testuser')
user.create()
self.assertEqual([
'groupadd testuser',
'usermod -d /testuser -g testuser testuser'
],
self.fakeCallAndRead.external_command_list)
def test_create_user_group_exists(self):
global USER_LIST
USER_LIST = ['testuser']
global GROUP_LIST
GROUP_LIST = ['testuser']
user = slapos.format.User('testuser')
user.setPath('/testuser')
user.create()
self.assertEqual([
'usermod -d /testuser -g testuser testuser'
],
self.fakeCallAndRead.external_command_list)
def test_isAvailable(self):
global USER_LIST
USER_LIST = ['testuser']
user = slapos.format.User('testuser')
self.assertTrue(user.isAvailable())
def test_isAvailable_notAvailable(self):
user = slapos.format.User('doesnotexistsyet')
self.assertFalse(user.isAvailable())
if __name__ == '__main__':
unittest.main()
slapos.core-1.3.10/slapos/tests/pyflakes/ 0000755 0000000 0000000 00000000000 12517721227 017011 5 ustar root root slapos.core-1.3.10/slapos/tests/pyflakes/__init__.py 0000644 0000000 0000000 00000004242 12517720512 021120 0 ustar root root ##############################################################################
#
# Copyright (c) 2013 Vifib SARL and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import os
import pkg_resources
import pyflakes.scripts.pyflakes
import sys
import unittest
class CheckCodeConsistency(unittest.TestCase):
"""Lints all SlapOS Node and SLAP library code base."""
def setUp(self):
self._original_argv = sys.argv
sys.argv = [sys.argv[0],
os.path.join(
pkg_resources.get_distribution('slapos.core').location,
'slapos',
)
]
def tearDown(self):
sys.argv = self._original_argv
@unittest.skip('pyflakes test is disabled')
def testCodeConsistency(self):
if pyflakes.scripts.pyflakes.main.func_code.co_argcount:
pyflakes.scripts.pyflakes.main([
os.path.join(
pkg_resources.get_distribution('slapos.core').location,
'slapos',
)])
else:
pyflakes.scripts.pyflakes.main()
slapos.core-1.3.10/slapos/__init__.py 0000644 0000000 0000000 00000000364 12517720512 016141 0 ustar root root # See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
try:
__import__('pkg_resources').declare_namespace(__name__)
except ImportError:
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
slapos.core-1.3.10/slapos/collect/ 0000755 0000000 0000000 00000000000 12517721227 015456 5 ustar root root slapos.core-1.3.10/slapos/collect/db.py 0000644 0000000 0000000 00000040423 12517720512 016414 0 ustar root root # -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import sqlite3
import os
from time import strftime
import datetime
class Database:
database_name = "collector.db"
table_list = ["user", "computer", "system", "disk", \
"temperature", "heating"]
preserve_table_list = ["heating"]
CREATE_USER_TABLE = "create table if not exists user " \
"(partition text, pid real, process text, " \
" cpu_percent real, cpu_time real, " \
" cpu_num_threads real, memory_percent real, " \
" memory_rss real, io_rw_counter real, " \
" io_cycles_counter real, date text, time text, " \
" reported integer NULL DEFAULT 0)"
CREATE_COMPUTER_TABLE = "create table if not exists computer "\
"(cpu_num_core real, cpu_frequency real, cpu_type text," \
" memory_size real, memory_type text, partition_list text," \
" date text, time text, reported integer NULL DEFAULT 0)"
CREATE_SYSTEM_TABLE = "create table if not exists system " \
"(loadavg real, cpu_percent real, memory_used real, "\
" memory_free real, net_in_bytes real, net_in_errors real, "\
" net_in_dropped real, net_out_bytes real, net_out_errors real, "\
" net_out_dropped real, date text, time text, " \
" reported integer NULL DEFAULT 0)"
CREATE_DISK_PARTITION = "create table if not exists disk "\
"(partition text, used text, free text, mountpoint text, " \
" date text, time text, reported integer NULL DEFAULT 0)"
CREATE_TEMPERATURE_TABLE = "create table if not exists temperature " \
"(sensor_id name, temperature real, alarm integer, "\
"date text, time text, reported integer NULL DEFAULT 0)"
CREATE_HEATING_TABLE = "create table if not exists heating " \
"(model_id name, sensor_id name, initial_temperature real, "\
" final_temperature real, delta_time real, zero_emission_ratio real, "\
"date text, time text, reported integer NULL DEFAULT 0)"
INSERT_USER_TEMPLATE = "insert into user(" \
"partition, pid, process, cpu_percent, cpu_time, " \
"cpu_num_threads, memory_percent," \
"memory_rss, io_rw_counter, io_cycles_counter, " \
"date, time) values " \
"('%s', %s, '%s', %s, %s, %s, %s, %s, %s, %s, '%s', '%s' )"
INSERT_COMPUTER_TEMPLATE = "insert into computer("\
" cpu_num_core, cpu_frequency, cpu_type," \
"memory_size, memory_type, partition_list," \
"date, time) values "\
"(%s, %s, '%s', %s, '%s', '%s', '%s', '%s' )"
INSERT_DISK_TEMPLATE = "insert into disk("\
" partition, used, free, mountpoint," \
" date, time) "\
"values ('%s', %s, %s, '%s', '%s', '%s' )"
INSERT_SYSTEM_TEMPLATE = "insert into system("\
" loadavg, cpu_percent, memory_used, memory_free," \
" net_in_bytes, net_in_errors, net_in_dropped," \
" net_out_bytes, net_out_errors, net_out_dropped, " \
" date, time) values "\
"( %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, '%s', '%s' )"
INSERT_TEMPERATURE_TEMPLATE = "insert into temperature("\
" sensor_id, temperature, alarm," \
" date, time) values "\
"( '%s', %s, %s, '%s', '%s' )"
INSERT_HEATING_TEMPLATE = "insert into heating("\
" model_id, sensor_id, initial_temperature, final_temperature, "\
" delta_time, zero_emission_ratio," \
" date, time) values "\
"( '%s', '%s', %s, %s, %s, %s, '%s', '%s' )"
def __init__(self, directory = None):
assert self.database_name is not None
self.uri = os.path.join(directory, self.database_name)
self.connection = None
self.cursor = None
self._bootstrap()
def connect(self):
self.connection = sqlite3.connect(self.uri)
self.cursor = self.connection.cursor()
def commit(self):
assert self.connection is not None
self.connection.commit()
def close(self):
assert self.connection is not None
self.cursor.close()
self.connection.close()
def _execute(self, sql):
assert self.connection is not None
return self.cursor.execute(sql)
def _bootstrap(self):
assert self.CREATE_USER_TABLE is not None
self.connect()
self._execute(self.CREATE_USER_TABLE)
self._execute(self.CREATE_COMPUTER_TABLE)
self._execute(self.CREATE_SYSTEM_TABLE)
self._execute(self.CREATE_DISK_PARTITION)
self._execute(self.CREATE_TEMPERATURE_TABLE)
self._execute(self.CREATE_HEATING_TABLE)
self.commit()
self.close()
def _getInsertionDateTuple(self):
return strftime("%Y-%m-d -- %H:%M:%S").split(" -- ")
###################
# Insertion methods
###################
def insertUserSnapshot(self, partition, pid, process, cpu_percent, cpu_time,
cpu_num_threads, memory_percent, memory_rss, io_rw_counter,
io_cycles_counter, insertion_date, insertion_time):
""" Insert user processes snapshots information on a database """
insertion_sql = self.INSERT_USER_TEMPLATE % \
( partition, pid, process, cpu_percent, cpu_time,
cpu_num_threads, memory_percent,
memory_rss, io_rw_counter, io_cycles_counter,
insertion_date, insertion_time)
self._execute(insertion_sql)
return insertion_sql
def insertComputerSnapshot(self, cpu_num_core, cpu_frequency, cpu_type,
memory_size, memory_type, partition_list, insertion_date, insertion_time):
"""Insert Computer general informations snapshots informations on
the database
"""
insertion_sql = self.INSERT_COMPUTER_TEMPLATE % \
( cpu_num_core, cpu_frequency, cpu_type,
memory_size, memory_type,
partition_list, insertion_date,
insertion_time)
self._execute(insertion_sql)
return insertion_sql
def insertDiskPartitionSnapshot(self, partition, used, free, mountpoint,
insertion_date, insertion_time):
""" Insert Disk Partitions informations on the database """
insertion_sql = self.INSERT_DISK_TEMPLATE % \
( partition, used, free, mountpoint,
insertion_date, insertion_time )
self._execute(insertion_sql)
return insertion_sql
def insertSystemSnapshot(self, loadavg, cpu_percent, memory_used, memory_free,
net_in_bytes, net_in_errors, net_in_dropped, net_out_bytes,
net_out_errors, net_out_dropped, insertion_date, insertion_time):
""" Include System general Snapshot on the database
"""
insertion_sql = self.INSERT_SYSTEM_TEMPLATE % \
( loadavg, cpu_percent, memory_used, memory_free,
net_in_bytes, net_in_errors, net_in_dropped,
net_out_bytes, net_out_errors, net_out_dropped,
insertion_date, insertion_time )
self._execute(insertion_sql)
return insertion_sql
def insertTemperatureSnapshot(self, sensor_id, temperature, alarm,
insertion_date, insertion_time):
""" Include Temperature information Snapshot on the database
"""
insertion_sql = self.INSERT_TEMPERATURE_TEMPLATE % \
(sensor_id, temperature, alarm, insertion_date, insertion_time)
self._execute(insertion_sql)
return insertion_sql
def insertHeatingSnapshot(self, model_id, sensor_id, initial_temperature,
final_temperature, delta_time, zero_emission_ratio,
insertion_date, insertion_time):
""" Include Heating information Snapshot on the database
"""
insertion_sql = self.INSERT_HEATING_TEMPLATE % \
(model_id, sensor_id, initial_temperature, final_temperature,
delta_time, zero_emission_ratio, insertion_date, insertion_time)
self._execute(insertion_sql)
return insertion_sql
def getTableList(self):
""" Get the list of tables from the database
"""
return [i[0] for i in self._execute(
"SELECT name FROM sqlite_master WHERE type='table'")]
def _getGarbageCollectionDateList(self, days_to_preserve=3):
""" Return the list of dates to Preserve when data collect
"""
base = datetime.datetime.today()
date_list = []
for x in range(0, days_to_preserve):
date_list.append((base - datetime.timedelta(days=x)).strftime("%Y-%m-%d"))
return date_list
def garbageCollect(self):
""" Garbase collect the database, by removing older records already
reported.
"""
date_list = self._getGarbageCollectionDateList()
where_clause = "reported = 1"
for _date in date_list:
where_clause += " AND date != '%s' " % _date
delete_sql = "DELETE FROM %s WHERE %s"
self.connect()
for table in self.table_list:
if table not in self.preserve_table_list:
self._execute(delete_sql % (table, where_clause))
self.commit()
self.close()
def getDateScopeList(self, ignore_date=None, reported=0):
""" Get from the present unique dates from the system
Use a smaller table to sabe time.
"""
if ignore_date is not None:
where_clause = " AND date != '%s'" % ignore_date
else:
where_clause = ""
select_sql = "SELECT date, count(time) FROM system "\
" WHERE reported = %s %s GROUP BY date" % \
(reported, where_clause)
return self._execute(select_sql)
def markDayAsReported(self, date_scope, table_list):
""" Mark all registers from a certain date as reported """
update_sql = "UPDATE %s SET reported = 1 " \
"WHERE date = '%s' AND reported = 0"
for table in table_list:
self._execute(update_sql % (table, date_scope))
def select(self, table, date=None, columns="*", where=None):
""" Query database for a full table information """
if date is not None:
where_clause = " WHERE date = '%s' " % date
else:
where_clause = ""
if where is not None:
if where_clause == "":
where_clause += " WHERE 1 = 1 "
where_clause += " AND %s " % where
select_sql = "SELECT %s FROM %s %s " % (columns, table, where_clause)
return self._execute(select_sql)
#####################################################
# Export Tables as Dict for handle realtime plotting
#####################################################
def exportSystemAsDict(self, date):
""" Export system table as dictionally, formatting the output
for present it in a nicer presentation.
"""
collected_entry_dict = {}
collected_entry_dict["loadavg"] = []
collected_entry_dict["cpu_percent"] = []
collected_entry_dict["memory_used"] = []
collected_entry_dict["memory_free"] = []
collected_entry_dict["net_in_bytes"] = []
collected_entry_dict["net_in_errors"] = []
collected_entry_dict["net_in_dropped"] = []
collected_entry_dict["net_out_bytes"] = []
collected_entry_dict["net_out_errors"] = []
collected_entry_dict["net_out_dropped"] = []
first_entry = 1
last_entry_in = 0
last_entry_out = 0
entry_list = self._execute(
"SELECT loadavg, cpu_percent, memory_used, memory_free," \
" net_in_bytes, net_in_errors, net_in_dropped," \
" net_out_bytes, net_out_errors, net_out_dropped, " \
" date, time FROM system WHERE date = '%s'" % date)
for entry in entry_list:
entry_time = "%s %s" % (entry[10], str(entry[11]))
if not first_entry:
_entry_in = entry[4] - last_entry_in
last_entry_in = entry[4]
entry_in = _entry_in
_entry_out = entry[7] - last_entry_out
last_entry_out = entry[7]
entry_out = _entry_out
else:
first_entry = 0
last_entry_in = entry[4]
last_entry_out = entry[7]
continue
collected_entry_dict["loadavg"].append(
{'entry': entry[0], 'time': entry_time })
collected_entry_dict["cpu_percent"].append(
{'entry': entry[1], 'time': entry_time })
collected_entry_dict["memory_used"].append(
{'entry': entry[2]/1024, 'time': entry_time })
collected_entry_dict["memory_free"].append(
{'entry': entry[3]/1024, 'time': entry_time })
collected_entry_dict["net_in_bytes"].append(
{'entry': entry_in/1024, 'time': entry_time })
collected_entry_dict["net_in_errors"].append(
{'entry': entry[5], 'time': entry_time })
collected_entry_dict["net_in_dropped"].append(
{'entry': entry[6], 'time': entry_time })
collected_entry_dict["net_out_bytes"].append(
{'entry': entry_out/1024, 'time': entry_time })
collected_entry_dict["net_out_errors"].append(
{'entry': entry[8], 'time': entry_time })
collected_entry_dict["net_out_dropped"].append(
{'entry': entry[9], 'time': entry_time })
return collected_entry_dict
def exportDiskAsDict(self, date):
""" Export a column from a table for a given date.
"""
collected_entry_dict = {}
entry_list = self._execute(
"SELECT partition, used, free, date, time "\
"from disk WHERE date = '%s'" % (date))
for partition, used, free, __date, __time in entry_list:
partition_used = "%s-used" % partition
partition_free = "%s-free" % partition
if partition_used not in collected_entry_dict:
collected_entry_dict[partition_used] = []
if partition_free not in collected_entry_dict:
collected_entry_dict[partition_free] = []
collected_entry_dict[partition_used].append(
{'entry': int(used)/1024,
'time': "%s %s" % (__date, str(__time))})
collected_entry_dict[partition_free].append(
{'entry': int(free)/1024,
'time': "%s %s" % (__date, str(__time))})
return collected_entry_dict
def getLastHeatingTestTime(self):
select_sql = "SELECT date, time FROM heating ORDER BY date, time DESC LIMIT 1"
for __date, __time in self._execute(select_sql):
_date = datetime.datetime.strptime("%s %s" % (__date, __time), "%Y-%m-%d %H:%M:%S")
return datetime.datetime.now() - _date
return datetime.timedelta(weeks=520)
def getLastZeroEmissionRatio(self):
select_sql = "SELECT zero_emission_ratio FROM heating ORDER BY date, time DESC LIMIT 1"
for entry in self._execute(select_sql):
return entry[0]
return -1
def getCollectedTemperatureList(self, sensor_id=None, limit=1):
""" Query database for a full table information """
if limit > 0:
limit_clause = "LIMIT %s" % (limit,)
else:
limit_clause = ""
if sensor_id is not None:
where_clause = "WHERE sensor_id = '%s'" % (sensor_id)
else:
where_clause = ""
select_sql = "SELECT * FROM temperature %s ORDER BY time DESC %s" % (where_clause, limit_clause)
return self._execute(select_sql)
slapos.core-1.3.10/slapos/collect/README.txt 0000644 0000000 0000000 00000017311 12517720512 017153 0 ustar root root
Collecting Data
================
The "slapos node collect" command collects data from a computer taking a
few snapshot on different scopes and storing it (currently on sqllite3).
Scopes of Snapshots are:
- User Processes: Collects data from all user's process related to SlapOS (ie.: slapuser*)
- System Information: Collects data from the System Usage and Computer Hardware.
So on every slapos node collect calls (perfomed by cron on every minute), the
slapos stores the all snapshots for future analizes.
User's Processes Snapshot
==========================
Collect command search for all process launched by all users related to the
slapos [1]. After this, for each process it uses psutil (or similars tools) to
collect all available information for every process pid [2].
Once Collected, every Process information is stored on sqllite3 [3], in other
words, we have 1 line per pid for a giving time. It's used pid number and
process creation date for create a UID for the process, and it is omitted the
command name in order to annonymalize the data (so the risk of information
leak is reduced).
The measuring of process only consider CPU, memory and io operations (rw and
cycles), we are studying how to measure network (without be intrusive).
System Information Snapshot
============================
Those snapshots has 2 different goals, first is collect current load from existing
computer (cpu, memory, disk, network...) and the second goal is collect the
available resources the computer has installed [4].
We use 3 types of snapshots for determinate the load and the available resources
(all mostly use psutils to collect data):
- System Snapshot [5]: It collects general computer usage like CPU, Memory
and Network IO usage.
- Computer Snapshot [6]: It collects for now number of CPU cores and available
memory, however we wish to collect more details.
- Disk Snapshot [7]: It collects the informations related to the a disk
(1 snapshot per disk), which contains total, usage and
io informations.
"Real-time" Partial dump (Dygraph)
===================================
On every run, we dump data from the current day on csv [8] (2 axes), in order to
plot easily with dygraph, so there will be few files available like this:
- system_cpu_percent.csv
- system_disk_memory_free__dev_sda1.csv
- system_disk_memory_free__dev_sdb1.csv
- system_disk_memory_used__dev_sda1.csv
- system_disk_memory_used__dev_sdb1.csv
- system_loadavg.csv
- system_memory_free.csv
- system_memory_used.csv
- system_net_in_bytes.csv
- system_net_in_dropped.csv
- system_net_in_errors.csv
- system_net_out_bytes.csv
- system_net_out_dropped.csv
- system_net_out_errors.csv
All contains only information from computer usage, for global usage (for now). It
is perfectly acceptable keep a realtime copy in csv of the most recently data.
Logrotate
=========
Slapos collects contains its on log rotating policy [9] and gargabe collection [10].
- We dump in folders YYYY-MM-DD, all data which are not from the current day.
- Every table generates 1 csv with the date from the dumped day.
- All dumped data is marked as reported on sqllite (column reported)
- All data which are older them 3 days and it is already reported is removed.
- All folders which contains dumped data is compressed in a tar.gz file.
Data Structure
===============
The header of the CSVs are not included on the dumped file (it is probably a
mistake), but it corresponds to (same as columns on the sqllite) which can be
easily described like bellow [11]:
- user
partition (text)
pid (real)
process (text)
cpu_percent (real)
cpu_time (real)
cpu_num_threads (real)
memory_percent (real)
memory_rss (real)
io_rw_counter (real)
io_cycles_counter (real)
date (text)
time (text)
reported (integer)
- computer
cpu_num_core (real)
cpu_frequency (real
cpu_type (text)
memory_size (real)
memory_type (text)
partition_list (text)
date (text)
time (text)
reported (integer)
- system
loadavg (real)
cpu_percent (real)
memory_used (real)
memory_free (real)
net_in_bytes (real)
net_in_errors (real)
net_in_dropped (real)
net_out_bytes (real)
net_out_errors (real)
net_out_dropped (real)
date (text)
time (text)
reported (integer)
- disk
partition (text)
used (text)
free (text)
mountpoint (text)
date (text)
time (text)
reported (integer)
Probably a more formal way to collect data data can be introduced.
Download Collected Data
========================
Data is normally available on the server file system, we use a simple software
"slapmonitor" which can be deployed on any machine which allow us download via
HTTP the data.
Slapmonitor can be also used to determinate de availability of the machine (it
returns "OK" if accessed on his "/" address), and it servers the data on a url
like:
- https:/// -> just return "OK"
- https:////server-log/ -> you can see all files
The slapmonitoring can be easily extented to include more sensors (like
temperature, benchmarks...) which normally requires more speficic software
configurations.
Planned Non core extensions and benchmarking
=============================================
It is planned to include 4 simple benchmarks measure machines performance
degradation overtime:
- CPU benchmark with Pystone
- SQL Benchmark on SQLlite (for now)
- Network Uplink Benchmark
- Network Download Benchmark
This part is not included or coded, but we intent to measure performance
degradation in future, to stop to allocate if the machine is working but
cannot mantain a minimal Service Quality (even if it is not looks like
overloaded).
Servers Availability
=====================
All servers contacts the slapos master on regular bases (several times a minute),
it is possible to determinate the general availability of a server by looking at
apache log using this script:
- http://git.erp5.org/gitweb/cloud-quote.git/blob/HEAD:/py/my.py
It produces a json like this:
- http://git.erp5.org/gitweb/cloud-quote.git/blob/HEAD:/data/stats.json
However, this is a bit draft and rudimentar to determinate problems on the
machine, as the machine completly "death" is rare, normally most of failures are
pure network problems or human/environmental problem (normally not depends of
the machine load).
[1] http://git.erp5.org/gitweb/slapos.core.git/blob/HEAD:/slapos/collect/entity.py?js=1#l58
[2] http://git.erp5.org/gitweb/slapos.core.git/blob/HEAD:/slapos/collect/snapshot.py?js=1#l37
[3] http://git.erp5.org/gitweb/slapos.core.git/blob/HEAD:/slapos/collect/db.py?js=1#l130
[4] http://git.erp5.org/gitweb/slapos.core.git/blob/HEAD:/slapos/collect/entity.py?js=1#l77
[5] http://git.erp5.org/gitweb/slapos.core.git/blob/HEAD:/slapos/collect/snapshot.py?js=1#l62
[6] http://git.erp5.org/gitweb/slapos.core.git/blob/HEAD:/slapos/collect/snapshot.py?js=1#l95
[7] http://git.erp5.org/gitweb/slapos.core.git/blob/HEAD:/slapos/collect/snapshot.py?js=1#l81
[8] http://git.erp5.org/gitweb/slapos.core.git/blob/HEAD:/slapos/collect/reporter.py?js=1#l75
[9] http://git.erp5.org/gitweb/slapos.core.git/blob/HEAD:/slapos/collect/reporter.py?js=1
[10] http://git.erp5.org/gitweb/slapos.core.git/blob/HEAD:/slapos/collect/db.py?js=1#l192
[11] http://git.erp5.org/gitweb/slapos.core.git/blob/HEAD:/slapos/collect/db.py?js=1#l39
slapos.core-1.3.10/slapos/collect/reporter.py 0000644 0000000 0000000 00000026564 12517720512 017703 0 ustar root root # -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from lxml import etree as ElementTree
from slapos.util import mkdir_p
import csv
import glob
import json
import os
import os.path
import shutil
import tarfile
import time
import psutil
log_file = False
class Dumper(object):
def __init__(self, database):
self.db = database
def dump(self, folder):
raise NotImplemented("Implemented on Subclass")
def writeFile(self, **kw):
raise NotImplemented("Implemented on Subclass")
class SystemReporter(Dumper):
def dump(self, folder):
""" Dump data """
_date = time.strftime("%Y-%m-%d")
self.db.connect()
for item, collected_item_list in self.db.exportSystemAsDict(_date).iteritems():
self.writeFile(item, folder, collected_item_list)
for partition, collected_item_list in self.db.exportDiskAsDict(_date).iteritems():
partition_id = "_".join(partition.split("-")[:-1]).replace("/", "_")
item = "memory_%s" % partition.split("-")[-1]
self.writeFile("disk_%s_%s" % (item, partition_id), folder, collected_item_list)
self.db.close()
class SystemJSONReporterDumper(SystemReporter):
def writeFile(self, name, folder, collected_entry_list=[]):
""" Dump data as json """
file_io = open(os.path.join(folder, "system_%s.json" % name), "w")
json.dump(collected_entry_list, file_io, sort_keys=True, indent=2)
file_io.close()
class SystemCSVReporterDumper(SystemReporter):
def writeFile(self, name, folder, collected_entry_list=[]):
""" Dump data as json """
file_io = open(os.path.join(folder, "system_%s.csv" % name), "w")
csv_output = csv.writer(file_io)
csv_output.writerow(["time", "entry"])
for collected_entry in collected_entry_list:
csv_output.writerow([collected_entry["time"], collected_entry["entry"]])
file_io.close()
class RawDumper(Dumper):
""" Dump raw data in a certain format
"""
def dump(self, folder):
date = time.strftime("%Y-%m-%d")
self.db.connect()
table_list = self.db.getTableList()
for date_scope, amount in self.db.getDateScopeList(ignore_date=date):
for table in table_list:
self.writeFile(table, folder, date_scope,
self.db.select(table, date_scope))
self.db.markDayAsReported(date_scope,
table_list=table_list)
self.db.commit()
self.db.close()
class RawCSVDumper(RawDumper):
def writeFile(self, name, folder, date_scope, rows):
mkdir_p(os.path.join(folder, date_scope), 0o755)
file_io = open(os.path.join(folder, "%s/dump_%s.csv" % (date_scope, name)), "w")
csv_output = csv.writer(file_io)
csv_output.writerows(rows)
file_io.close()
def compressLogFolder(log_directory):
initial_folder = os.getcwd()
os.chdir(log_directory)
try:
for backup_to_archive in glob.glob("*-*-*/"):
filename = '%s.tar.gz' % backup_to_archive.strip("/")
with tarfile.open(filename, 'w:gz') as tfile:
tfile.add(backup_to_archive)
tfile.close()
shutil.rmtree(backup_to_archive)
finally:
os.chdir(initial_folder)
class ConsumptionReport(object):
def __init__(self, database, computer_id, location, user_list):
self.computer_id = computer_id
self.db = database
self.user_list = user_list
self.location = location
def buildXMLReport(self, date_scope):
xml_report_path = "%s/%s.xml" % (self.location, date_scope)
if os.path.exists(xml_report_path):
return
if os.path.exists('%s.uploaded' % xml_report_path):
return
journal = Journal()
transaction = journal.newTransaction()
journal.setProperty(transaction, "title", "Eco Information for %s " % self.computer_id)
journal.setProperty(transaction, "start_date", "%s 00:00:00" % date_scope)
journal.setProperty(transaction, "stop_date", "%s 23:59:59" % date_scope)
journal.setProperty(transaction, "reference", "%s-global" % date_scope)
journal.setProperty(transaction, "currency", "")
journal.setProperty(transaction, "payment_mode", "")
journal.setProperty(transaction, "category", "")
arrow = ElementTree.SubElement(transaction, "arrow")
arrow.set("type", "Destination")
cpu_load_percent = self._getCpuLoadAverageConsumption(date_scope)
if cpu_load_percent is not None:
journal.newMovement(transaction,
resource="service_module/cpu_load_percent",
title="CPU Load Percent Average",
quantity=str(cpu_load_percent),
reference=self.computer_id,
category="")
memory_used = self._getMemoryAverageConsumption(date_scope)
if memory_used is not None:
journal.newMovement(transaction,
resource="service_module/memory_used",
title="Used Memory",
quantity=str(memory_used),
reference=self.computer_id,
category="")
if self._getZeroEmissionContribution() is not None:
journal.newMovement(transaction,
resource="service_module/zero_emission_ratio",
title="Zero Emission Ratio",
quantity=str(self._getZeroEmissionContribution()),
reference=self.computer_id,
category="")
core_amount = psutil.NUM_CPUS
for user in self.user_list:
partition_cpu_load_percent = self._getPartitionCPULoadAverage(user, date_scope)
if partition_cpu_load_percent is not None:
journal.newMovement(transaction,
resource="service_module/cpu_load_percent",
title="CPU Load Percent Average for %s" % (user),
quantity=str(partition_cpu_load_percent/core_amount),
reference=user,
category="")
mb = float(2 ** 20)
for user in self.user_list:
partition_memory_used = self._getPartitionUsedMemoryAverage(user, date_scope)
if partition_memory_used is not None:
journal.newMovement(transaction,
resource="service_module/memory_used",
title="Memory Used Average for %s" % (user),
quantity=str(partition_memory_used/mb),
reference=user,
category="")
with open(xml_report_path, 'w') as f:
f.write(journal.getXML())
f.close()
return xml_report_path
def _getAverageFromList(self, data_list):
return sum(data_list)/len(data_list)
def _getCpuLoadAverageConsumption(self, date_scope):
self.db.connect()
query_result_cursor = self.db.select("system", date_scope,
columns="SUM(cpu_percent)/COUNT(cpu_percent)")
cpu_load_percent_list = zip(*query_result_cursor)
self.db.close()
if len(cpu_load_percent_list):
return cpu_load_percent_list[0][0]
def _getMemoryAverageConsumption(self, date_scope):
self.db.connect()
query_result_cursor = self.db.select("system", date_scope,
columns="SUM(memory_used)/COUNT(memory_used)")
memory_used_list = zip(*query_result_cursor)
self.db.close()
if len(memory_used_list):
return memory_used_list[0][0]
def _getZeroEmissionContribution(self):
self.db.connect()
zer = self.db.getLastZeroEmissionRatio()
self.db.close()
return zer
def _getPartitionCPULoadAverage(self, partition_id, date_scope):
self.db.connect()
query_result_cursor = self.db.select("user", date_scope,
columns="SUM(cpu_percent)",
where="partition = '%s'" % partition_id)
cpu_percent_sum = zip(*query_result_cursor)
if len(cpu_percent_sum) and cpu_percent_sum[0][0] is None:
return
query_result_cursor = self.db.select("user", date_scope,
columns="COUNT(DISTINCT time)",
where="partition = '%s'" % partition_id)
sample_amount = zip(*query_result_cursor)
self.db.close()
if len(sample_amount) and len(cpu_percent_sum):
return cpu_percent_sum[0][0]/sample_amount[0][0]
def _getPartitionUsedMemoryAverage(self, partition_id, date_scope):
self.db.connect()
query_result_cursor = self.db.select("user", date_scope,
columns="SUM(memory_rss)",
where="partition = '%s'" % partition_id)
memory_sum = zip(*query_result_cursor)
if len(memory_sum) and memory_sum[0][0] is None:
return
query_result_cursor = self.db.select("user", date_scope,
columns="COUNT(DISTINCT time)",
where="partition = '%s'" % partition_id)
sample_amount = zip(*query_result_cursor)
self.db.close()
if len(sample_amount) and len(memory_sum):
return memory_sum[0][0]/sample_amount[0][0]
class Journal(object):
def __init__(self):
self.root = ElementTree.Element("journal")
def getXML(self):
report = ElementTree.tostring(self.root)
return "%s" % report
def newTransaction(self, portal_type="Sale Packing List"):
transaction = ElementTree.SubElement(self.root, "transaction")
transaction.set("type", portal_type)
return transaction
def setProperty(self, element, name, value):
property_element = ElementTree.SubElement(element, name)
property_element.text = value
def newMovement(self, transaction, resource, title,
quantity, reference, category):
movement = ElementTree.SubElement(transaction, "movement")
self.setProperty(movement, "resource", resource)
self.setProperty(movement, "title", title)
self.setProperty(movement, "reference", reference)
self.setProperty(movement, "quantity", quantity)
self.setProperty(movement, "price", "0.0")
self.setProperty(movement, "VAT", "")
# Provide units
self.setProperty(movement, "category", category)
return movement
slapos.core-1.3.10/slapos/collect/__init__.py 0000644 0000000 0000000 00000013501 12517720512 017563 0 ustar root root # -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from psutil import process_iter, NoSuchProcess, AccessDenied
from time import strftime
import shutil
import datetime
from slapos.collect.db import Database
from slapos.util import mkdir_p
import os
import stat
from slapos.collect.snapshot import ProcessSnapshot, ComputerSnapshot
from slapos.collect.reporter import RawCSVDumper, \
SystemCSVReporterDumper, \
compressLogFolder, \
ConsumptionReport
from entity import get_user_list, Computer
def _get_time():
return strftime("%Y-%m-%d -- %H:%M:%S").split(" -- ")
def build_snapshot(proc):
try:
return ProcessSnapshot(proc)
except NoSuchProcess:
return None
def _get_uptime():
# Linux only
if os.path.exists('/proc/uptime'):
with open('/proc/uptime', 'r') as f:
return datetime.timedelta(seconds=float(f.readline().split()[0]))
def current_state(user_dict):
"""
Iterator used to apply build_snapshot(...) on every single relevant process.
A process is considered relevant if its user matches our user list, i.e.
its user is a slapos user
"""
process_list = [p for p in process_iter() if p.username() in user_dict]
for i, process in enumerate(process_list):
yield build_snapshot(process)
def do_collect(conf):
"""
Main function
The idea here is to poll system every so many seconds
For each poll, we get a list of Snapshots, holding informations about
processes. We iterate over that list to store datas on a per user basis:
Each user object is a dict, indexed on timestamp. We add every snapshot
matching the user so that we get informations for each users
"""
try:
collected_date, collected_time = _get_time()
user_dict = get_user_list(conf)
try:
for snapshot in current_state(user_dict):
if snapshot:
user_dict[snapshot.username].append(snapshot)
except (KeyboardInterrupt, SystemExit, NoSuchProcess):
raise
log_directory = "%s/var/data-log" % conf.get("slapos", "instance_root")
mkdir_p(log_directory, 0o755)
consumption_report_directory = "%s/var/consumption-report" % \
conf.get("slapos", "instance_root")
mkdir_p(consumption_report_directory, 0o755)
xml_report_directory = "%s/var/xml_report/%s" % \
(conf.get("slapos", "instance_root"),
conf.get("slapos", "computer_id"))
mkdir_p(xml_report_directory, 0o755)
if stat.S_IMODE(os.stat(log_directory).st_mode) != 0o755:
os.chmod(log_directory, 0o755)
database = Database(log_directory)
if conf.has_option("slapformat", "computer_model_id"):
computer_model_id = conf.get("slapformat",
"computer_model_id")
else:
computer_model_id = "no_model"
uptime = _get_uptime()
if conf.has_option("slapformat", "heating_sensor_id"):
heating_sensor_id = conf.get("slapformat",
"heating_sensor_id")
database.connect()
test_heating = uptime is not None and \
uptime > datetime.timedelta(seconds=86400) and \
database.getLastHeatingTestTime() > uptime
database.close()
else:
heating_sensor_id = "no_sensor"
test_heating = False
computer = Computer(ComputerSnapshot(model_id=computer_model_id,
sensor_id = heating_sensor_id,
test_heating=test_heating))
computer.save(database, collected_date, collected_time)
for user in user_dict.values():
user.save(database, collected_date, collected_time)
SystemCSVReporterDumper(database).dump(log_directory)
RawCSVDumper(database).dump(log_directory)
consumption_report = ConsumptionReport(
computer_id=conf.get("slapos", "computer_id"),
user_list=get_user_list(conf),
database=database,
location=consumption_report_directory)
base = datetime.datetime.today()
for x in range(1, 3):
report_file = consumption_report.buildXMLReport(
(base - datetime.timedelta(days=x)).strftime("%Y-%m-%d"))
if report_file is not None:
shutil.copy(report_file, xml_report_directory)
compressLogFolder(log_directory)
# Drop older entries already reported
database.garbageCollect()
except AccessDenied:
print "You HAVE TO execute this script with root permission."
slapos.core-1.3.10/slapos/collect/temperature/ 0000755 0000000 0000000 00000000000 12517721227 020013 5 ustar root root slapos.core-1.3.10/slapos/collect/temperature/__init__.py 0000644 0000000 0000000 00000007615 12517720512 022131 0 ustar root root
from multiprocessing import Process, active_children, cpu_count, Pipe
import subprocess
import os
import signal
import sys
import time
FIB_N = 100
DEFAULT_TIME = 60
try:
DEFAULT_CPU = cpu_count()
except NotImplementedError:
DEFAULT_CPU = 1
def collectComputerTemperature(sensor_bin="sensors"):
cmd = ["%s -u" % sensor_bin]
sp = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
stdout, stderr = sp.communicate()
sensor_output_list = stdout.splitlines()
adapter_name = ""
sensor_temperature_list = []
for line_number in range(len(sensor_output_list)):
found_sensor = None
stripped_line = sensor_output_list[line_number].strip()
if stripped_line.startswith("Adapter:"):
adapter_name = sensor_output_list[line_number-1]
elif stripped_line.startswith("temp") and "_input" in stripped_line:
temperature = sensor_output_list[line_number].split()[-1]
found_sensor = ["%s %s" % (adapter_name, sensor_output_list[line_number-1]), float(temperature)]
if found_sensor is not None:
critical = '1000'
maximal = '1000'
for next_line in sensor_output_list[line_number+1:line_number+3]:
stripped_next_line = next_line.strip()
if stripped_next_line.startswith("temp") and "_max" in stripped_next_line:
maximal = stripped_next_line.split()[-1]
elif stripped_next_line.startswith("temp") and "_crit" in stripped_next_line:
critical = stripped_next_line.split()[-1]
found_sensor.extend([float(maximal), float(critical)])
found_sensor.append(checkAlarm(float(temperature), float(maximal), float(critical)))
sensor_temperature_list.append(found_sensor)
return sensor_temperature_list
def checkAlarm(temperature, maximal, critical):
"""
Returns :
O if the temperature is below the maximal limit.
1 if the temperature is above the maximal limit.
2 if the temperature is above the crical limit.
"""
alarm = 0
if temperature >= maximal:
alarm += 1
if temperature >= critical:
alarm += 1
return alarm
def loop(connection):
connection.send(os.getpid())
connection.close()
while True:
fib(FIB_N)
def fib(n):
if n < 2:
return 1
else:
return fib(n - 1) + fib(n - 2)
def sigint_handler(signum, frame):
procs = active_children()
for p in procs:
p.terminate()
os._exit(1)
def launchTemperatureTest(sensor_id, sensor_bin="sensors", timeout=600, interval=30):
signal.signal(signal.SIGINT, sigint_handler)
def getTemperatureForSensor(s_id):
for collected_temperature in collectComputerTemperature(sensor_bin):
if collected_temperature[0] == sensor_id:
return collected_temperature[1], collected_temperature[4]
return None, None
process_list = []
process_connection_list = []
begin_time = time.time()
initial_temperature, alarm = getTemperatureForSensor(sensor_id)
if initial_temperature is None:
return
if alarm > 0:
# Skip to test if temperature is too high, because we cannot
# measure appropriatetly.
return
candidate_temperature = initial_temperature
for i in range(DEFAULT_CPU):
parent_connection, child_connection = Pipe()
process = Process(target=loop, args=(child_connection,))
process.start()
process_list.append(process)
process_connection_list.append(parent_connection)
for connection in process_connection_list:
try:
print connection.recv()
except EOFError:
continue
time.sleep(interval)
current_temperature = getTemperatureForSensor(sensor_id)
while current_temperature > candidate_temperature:
candidate_temperature = current_temperature
time.sleep(interval)
current_temperature = getTemperatureForSensor(sensor_id)
for process in process_list:
process.terminate()
return initial_temperature, current_temperature, time.time() - begin_time
slapos.core-1.3.10/slapos/collect/temperature/heating.py 0000644 0000000 0000000 00000000723 12517720512 022002 0 ustar root root
CONTRIBUTION_MAPPING = {
"shuttle_ds61_i7" : 0.045,
"nuc_i7": 0.055
}
def get_contribution_ratio(model_id, contribution):
zero_emission_ratio_limit = CONTRIBUTION_MAPPING.get(model_id)
if zero_emission_ratio_limit is None:
raise ValueError("Unknown heating contibution")
if contribution < zero_emission_ratio_limit:
# The machine don't contribute for heating
return 0
else:
# The machine contributes for the heating
return 100
slapos.core-1.3.10/slapos/collect/entity.py 0000644 0000000 0000000 00000015201 12517720512 017337 0 ustar root root # -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
def get_user_list(config):
nb_user = int(config.get("slapformat", "partition_amount"))
name_prefix = config.get("slapformat", "user_base_name")
path_prefix = config.get("slapformat", "partition_base_name")
instance_root = config.get("slapos", "instance_root")
user_dict = {name: User(name, path)
for name, path in [
(
"%s%s" % (name_prefix, nb),
"%s/%s%s" % (instance_root, path_prefix, nb)
) for nb in range(nb_user)
]
}
#user_dict['root'] = User("root", "/opt/slapgrid")
return user_dict
class User(object):
def __init__(self, name, path):
self.name = str(name)
self.path = str(path)
self.snapshot_list = []
def append(self, value):
self.snapshot_list.append(value)
def save(self, database, collected_date, collected_time):
""" Insert collected data on user collector """
database.connect()
for snapshot_item in self.snapshot_list:
snapshot_item.update_cpu_percent()
database.insertUserSnapshot(self.name,
pid=snapshot_item.get("pid"),
process=snapshot_item.get("process"),
cpu_percent=snapshot_item.get("cpu_percent"),
cpu_time=snapshot_item.get("cpu_time"),
cpu_num_threads=snapshot_item.get("cpu_num_threads"),
memory_percent=snapshot_item.get("memory_percent"),
memory_rss=snapshot_item.get("memory_rss"),
io_rw_counter=snapshot_item.get("io_rw_counter"),
io_cycles_counter=snapshot_item.get("io_cycles_counter"),
insertion_date=collected_date,
insertion_time=collected_time)
database.commit()
database.close()
class Computer(dict):
def __init__(self, computer_snapshot):
self.computer_snapshot = computer_snapshot
def save(self, database, collected_date, collected_time):
database.connect()
self._save_computer_snapshot(database, collected_date, collected_time)
self._save_system_snapshot(database, collected_date, collected_time)
self._save_disk_partition_snapshot(database, collected_date, collected_time)
self._save_temperature_snapshot(database, collected_date, collected_time)
self._save_heating_snapshot(database, collected_date, collected_time)
database.commit()
database.close()
def _save_computer_snapshot(self, database, collected_date, collected_time):
partition_list = ";".join(["%s=%s" % (x,y) for x,y in \
self.computer_snapshot.get("partition_list")])
database.insertComputerSnapshot(
cpu_num_core=self.computer_snapshot.get("cpu_num_core"),
cpu_frequency=self.computer_snapshot.get("cpu_frequency"),
cpu_type=self.computer_snapshot.get("cpu_type"),
memory_size=self.computer_snapshot.get("memory_size"),
memory_type=self.computer_snapshot.get("memory_type"),
partition_list=partition_list,
insertion_date=collected_date,
insertion_time=collected_time)
def _save_system_snapshot(self, database, collected_date, collected_time):
snapshot = self.computer_snapshot.get("system_snapshot")
database.insertSystemSnapshot(
loadavg=snapshot.get("load"),
cpu_percent=snapshot.get("cpu_percent"),
memory_used=snapshot.get("memory_used"),
memory_free=snapshot.get("memory_free"),
net_in_bytes=snapshot.get("net_in_bytes"),
net_in_errors=snapshot.get("net_in_errors"),
net_in_dropped=snapshot.get("net_in_dropped"),
net_out_bytes=snapshot.get("net_out_bytes"),
net_out_errors= snapshot.get("net_out_errors"),
net_out_dropped=snapshot.get("net_out_dropped"),
insertion_date=collected_date,
insertion_time=collected_time)
def _save_disk_partition_snapshot(self, database, collected_date, collected_time):
for disk_partition in self.computer_snapshot.get("disk_snapshot_list"):
database.insertDiskPartitionSnapshot(
partition=disk_partition.partition,
used=disk_partition.disk_size_used,
free=disk_partition.disk_size_free,
mountpoint=';'.join(disk_partition.mountpoint_list),
insertion_date=collected_date,
insertion_time=collected_time)
def _save_temperature_snapshot(self, database, collected_date, collected_time):
for temperature_snapshot in self.computer_snapshot.get("temperature_snapshot_list"):
database.insertTemperatureSnapshot(
sensor_id=temperature_snapshot.sensor_id,
temperature=temperature_snapshot.temperature,
alarm=temperature_snapshot.alarm,
insertion_date=collected_date,
insertion_time=collected_time)
def _save_heating_snapshot(self, database, collected_date, collected_time):
heating_snapshot = self.computer_snapshot.get("heating_contribution_snapshot")
if heating_snapshot is not None and \
heating_snapshot.initial_temperature is not None:
database.insertHeatingSnapshot(
initial_temperature=heating_snapshot.initial_temperature,
final_temperature=heating_snapshot.final_temperature,
delta_time=heating_snapshot.delta_time,
model_id=heating_snapshot.model_id,
sensor_id=heating_snapshot.sensor_id,
zero_emission_ratio=heating_snapshot.zero_emission_ratio,
insertion_date=collected_date,
insertion_time=collected_time)
slapos.core-1.3.10/slapos/collect/snapshot.py 0000644 0000000 0000000 00000016014 12517720512 017665 0 ustar root root # -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2010-2014 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import psutil
import os
from temperature import collectComputerTemperature, \
launchTemperatureTest
from temperature.heating import get_contribution_ratio
MEASURE_INTERVAL = 5
class _Snapshot(object):
def get(self, property, default=None):
return getattr(self, property, default)
class ProcessSnapshot(_Snapshot):
""" Take a snapshot from the running process
"""
def __init__(self, process=None):
assert type(process) is psutil.Process
ui_counter_list = process.get_io_counters()
self.username = process.username()
self.process_object = process
self.pid = process.pid
# Save full command line from the process.
self.process = "%s-%s" % (process.pid, process.create_time())
# CPU percentage, we will have to get actual absolute value
self.cpu_percent = self.process_object.get_cpu_percent(None)
# CPU Time
self.cpu_time = sum(process.get_cpu_times())
# Thread number, might not be really relevant
self.cpu_num_threads = process.get_num_threads()
# Memory percentage
self.memory_percent = process.get_memory_percent()
# Resident Set Size, virtual memory size is not accouned for
self.memory_rss = process.get_memory_info()[0]
# Byte count, Read and write. OSX NOT SUPPORTED
self.io_rw_counter = ui_counter_list[2] + ui_counter_list[3]
# Read + write IO cycles
self.io_cycles_counter = ui_counter_list[0] + ui_counter_list[1]
def update_cpu_percent(self):
if self.process_object.is_running():
# CPU percentage, we will have to get actual absolute value
self.cpu_percent = self.process_object.get_cpu_percent()
class SystemSnapshot(_Snapshot):
""" Take a snapshot from current system usage
"""
def __init__(self, interval=MEASURE_INTERVAL):
cpu_idle_percentage = psutil.cpu_times_percent(interval=interval).idle
load_percent = 100 - cpu_idle_percentage
memory = psutil.phymem_usage()
net_io = psutil.net_io_counters()
self.memory_used = memory.used
self.memory_free = memory.free
self.memory_percent = memory.percent
#self.cpu_percent = psutil.cpu_percent()
self.cpu_percent = load_percent
self.load = os.getloadavg()[0]
self.net_in_bytes = net_io.bytes_recv
self.net_in_errors = net_io.errin
self.net_in_dropped = net_io.dropin
self.net_out_bytes = net_io.bytes_sent
self.net_out_errors = net_io.errout
self.net_out_dropped = net_io.dropout
class TemperatureSnapshot(_Snapshot):
""" Take a snapshot from the current temperature on
all available sensors
"""
def __init__(self, sensor_id, temperature, alarm):
self.sensor_id = sensor_id
self.temperature = temperature
self.alarm = alarm
class HeatingContributionSnapshot(_Snapshot):
def __init__(self, sensor_id, model_id):
self.initial_temperature = None
result = launchTemperatureTest(sensor_id)
if result is None:
print "Impossible to test sensor: %s " % sensor_id
initial_temperature, final_temperature, duration = result
self.initial_temperature = initial_temperature
self.final_temperature = final_temperature
self.delta_time = duration
self.model_id = model_id
self.sensor_id = sensor_id
self.zero_emission_ratio = self._get_contribution_ratio()
def _get_contribution_ratio(self):
delta_temperature = (self.final_temperature-self.initial_temperature)
contribution_value = delta_temperature/self.delta_time
return get_contribution_ratio(self.model_id, contribution_value)
def _get_uptime(self):
# Linux only
if os.path.exists('/proc/uptime'):
with open('/proc/uptime', 'r') as f:
return float(f.readline().split()[0])
return -1
class DiskPartitionSnapshot(_Snapshot):
""" Take Snapshot from general disk partitions
usage
"""
def __init__(self, partition, mountpoint):
self.partition = partition
self.mountpoint_list = [ mountpoint ]
disk = psutil.disk_usage(mountpoint)
disk_io = psutil.disk_io_counters()
self.disk_size_used = disk.used
self.disk_size_free = disk.free
self.disk_size_percent = disk.percent
class ComputerSnapshot(_Snapshot):
""" Take a snapshot from computer informations
"""
def __init__(self, model_id=None, sensor_id=None, test_heating=False):
self.cpu_num_core = psutil.NUM_CPUS
self.cpu_frequency = 0
self.cpu_type = 0
self.memory_size = psutil.TOTAL_PHYMEM
self.memory_type = 0
#
# Include a SystemSnapshot and a list DiskPartitionSnapshot
# on a Computer Snapshot
#
self.system_snapshot = SystemSnapshot()
self.temperature_snapshot_list = self._get_temperature_snapshot_list()
self.disk_snapshot_list = []
self.partition_list = self._get_physical_disk_info()
if test_heating and model_id is not None \
and sensor_id is not None:
self.heating_contribution_snapshot = HeatingContributionSnapshot(sensor_id, model_id)
def _get_temperature_snapshot_list(self):
temperature_snapshot_list = []
for sensor_entry in collectComputerTemperature():
sensor_id, temperature, maximal, critical, alarm = sensor_entry
temperature_snapshot_list.append(
TemperatureSnapshot(sensor_id, temperature, alarm))
return temperature_snapshot_list
def _get_physical_disk_info(self):
partition_dict = {}
for partition in psutil.disk_partitions():
if partition.device not in partition_dict:
usage = psutil.disk_usage(partition.mountpoint)
partition_dict[partition.device] = usage.total
self.disk_snapshot_list.append(
DiskPartitionSnapshot(partition.device,
partition.mountpoint))
return [(k, v) for k, v in partition_dict.iteritems()]
slapos.core-1.3.10/slapos/slap/ 0000755 0000000 0000000 00000000000 12517721227 014770 5 ustar root root slapos.core-1.3.10/slapos/slap/util.py 0000644 0000000 0000000 00000000742 12517720512 016316 0 ustar root root from lxml import etree
def xml2dict(xml):
result_dict = {}
if xml is not None and xml != '':
tree = etree.fromstring(xml.encode('utf-8'))
for element in tree.iter(tag=etree.Element):
if element.tag == 'parameter':
key = element.get('id')
value = result_dict.get(key, None)
if value is not None:
value = value + ' ' + element.text
else:
value = element.text
result_dict[key] = value
return result_dict
slapos.core-1.3.10/slapos/slap/__init__.py 0000644 0000000 0000000 00000003044 12517720512 017076 0 ustar root root ##############################################################################
#
# Copyright (c) 2010, 2011, 2012 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import sys
if sys.version_info < (2, 6):
import warnings
warnings.warn('Used python version (%s) is old and has problems with'
' IPv6 connections' % '.'.join([str(q) for q in sys.version_info[:3]]))
from slap import *
slapos.core-1.3.10/slapos/slap/slap.py 0000644 0000000 0000000 00000111253 12517720512 016300 0 ustar root root # -*- coding: utf-8 -*-
# vim: set et sts=2:
##############################################################################
#
# Copyright (c) 2010, 2011, 2012 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
"""
Simple, easy to (un)marshall classes for slap client/server communication
"""
__all__ = ["slap", "ComputerPartition", "Computer", "SoftwareRelease",
"SoftwareInstance", "SoftwareProductCollection",
"Supply", "OpenOrder", "NotFoundError",
"ResourceNotReady", "ServerError", "ConnectionError"]
import json
import logging
import re
import urlparse
from util import xml2dict
import netaddr
from xml.sax import saxutils
import zope.interface
from interface import slap as interface
from xml_marshaller import xml_marshaller
from uritemplate import expand
import requests
# silence messages like 'Unverified HTTPS request is being made'
requests.packages.urllib3.disable_warnings()
# silence messages like 'Starting connection' that are logged with INFO
urllib3_logger = logging.getLogger('requests.packages.urllib3')
urllib3_logger.setLevel(logging.WARNING)
# XXX fallback_logger to be deprecated together with the old CLI entry points.
fallback_logger = logging.getLogger(__name__)
fallback_handler = logging.StreamHandler()
fallback_logger.setLevel(logging.INFO)
fallback_logger.addHandler(fallback_handler)
DEFAULT_SOFTWARE_TYPE = 'RootSoftwareInstance'
class SlapDocument:
def __init__(self, connection_helper=None, hateoas_navigator=None):
if connection_helper is not None:
# Do not require connection_helper to be provided, but when it's not,
# cause failures when accessing _connection_helper property.
self._connection_helper = connection_helper
self._hateoas_navigator = hateoas_navigator
class SlapRequester(SlapDocument):
"""
Abstract class that allow to factor method for subclasses that use "request()"
"""
def _requestComputerPartition(self, request_dict):
try:
xml = self._connection_helper.POST('requestComputerPartition', data=request_dict)
except ResourceNotReady:
return ComputerPartition(
request_dict=request_dict,
connection_helper=self._connection_helper,
)
if type(xml) is unicode:
xml = str(xml)
xml.encode('utf-8')
software_instance = xml_marshaller.loads(xml)
computer_partition = ComputerPartition(
software_instance.slap_computer_id.encode('UTF-8'),
software_instance.slap_computer_partition_id.encode('UTF-8'),
connection_helper=self._connection_helper,
)
# Hack to give all object attributes to the ComputerPartition instance
# XXX Should be removed by correctly specifying difference between
# ComputerPartition and SoftwareInstance
computer_partition.__dict__ = dict(computer_partition.__dict__.items() +
software_instance.__dict__.items())
# XXX not generic enough.
if xml_marshaller.loads(request_dict['shared_xml']):
computer_partition._synced = True
computer_partition._connection_dict = software_instance._connection_dict
computer_partition._parameter_dict = software_instance._parameter_dict
return computer_partition
class SoftwareRelease(SlapDocument):
"""
Contains Software Release information
"""
zope.interface.implements(interface.ISoftwareRelease)
def __init__(self, software_release=None, computer_guid=None, **kw):
"""
Makes easy initialisation of class parameters
XXX **kw args only kept for compatibility
"""
SlapDocument.__init__(self, kw.pop('connection_helper', None),
kw.pop('hateoas_navigator', None))
self._software_instance_list = []
if software_release is not None:
software_release = software_release.encode('UTF-8')
self._software_release = software_release
self._computer_guid = computer_guid
def __getinitargs__(self):
return (self._software_release, self._computer_guid, )
def getComputerId(self):
if not self._computer_guid:
raise NameError('computer_guid has not been defined.')
else:
return self._computer_guid
def getURI(self):
if not self._software_release:
raise NameError('software_release has not been defined.')
else:
return self._software_release
def error(self, error_log, logger=None):
try:
# Does not follow interface
self._connection_helper.POST('softwareReleaseError', data={
'url': self.getURI(),
'computer_id': self.getComputerId(),
'error_log': error_log})
except Exception:
(logger or fallback_logger).exception('')
def available(self):
self._connection_helper.POST('availableSoftwareRelease', data={
'url': self.getURI(),
'computer_id': self.getComputerId()})
def building(self):
self._connection_helper.POST('buildingSoftwareRelease', data={
'url': self.getURI(),
'computer_id': self.getComputerId()})
def destroyed(self):
self._connection_helper.POST('destroyedSoftwareRelease', data={
'url': self.getURI(),
'computer_id': self.getComputerId()})
def getState(self):
return getattr(self, '_requested_state', 'available')
class SoftwareProductCollection(object):
zope.interface.implements(interface.ISoftwareProductCollection)
def __init__(self, logger, slap):
self.logger = logger
self.slap = slap
self.get = self.__getattr__
def __getattr__(self, software_product):
self.logger.info('Getting best Software Release corresponging to '
'this Software Product...')
software_release_list = \
self.slap.getSoftwareReleaseListFromSoftwareProduct(software_product)
try:
software_release_url = software_release_list[0] # First is best one.
self.logger.info('Found as %s.' % software_release_url)
return software_release_url
except IndexError:
raise AttributeError('No Software Release corresponding to this '
'Software Product has been found.')
# XXX What is this SoftwareInstance class?
class SoftwareInstance(SlapDocument):
"""
Contains Software Instance information
"""
zope.interface.implements(interface.ISoftwareInstance)
def __init__(self, **kwargs):
"""
Makes easy initialisation of class parameters
"""
for k, v in kwargs.iteritems():
setattr(self, k, v)
"""Exposed exceptions"""
class ResourceNotReady(Exception):
zope.interface.implements(interface.IResourceNotReady)
class ServerError(Exception):
zope.interface.implements(interface.IServerError)
class NotFoundError(Exception):
zope.interface.implements(interface.INotFoundError)
class AuthenticationError(Exception):
pass
class ConnectionError(Exception):
zope.interface.implements(interface.IConnectionError)
class Supply(SlapDocument):
zope.interface.implements(interface.ISupply)
def supply(self, software_release, computer_guid=None, state='available'):
try:
self._connection_helper.POST('supplySupply', data={
'url': software_release,
'computer_id': computer_guid,
'state': state})
except NotFoundError:
raise NotFoundError("Computer %s has not been found by SlapOS Master."
% computer_guid)
class OpenOrder(SlapRequester):
zope.interface.implements(interface.IOpenOrder)
def request(self, software_release, partition_reference,
partition_parameter_kw=None, software_type=None,
filter_kw=None, state=None, shared=False):
if partition_parameter_kw is None:
partition_parameter_kw = {}
if filter_kw is None:
filter_kw = {}
request_dict = {
'software_release': software_release,
'partition_reference': partition_reference,
'partition_parameter_xml': xml_marshaller.dumps(partition_parameter_kw),
'filter_xml': xml_marshaller.dumps(filter_kw),
# XXX Cedric: Why state and shared are marshalled? First is a string
# And second is a boolean.
'state': xml_marshaller.dumps(state),
'shared_xml': xml_marshaller.dumps(shared),
}
if software_type is not None:
request_dict['software_type'] = software_type
else:
# Let's enforce a default software type
request_dict['software_type'] = DEFAULT_SOFTWARE_TYPE
return self._requestComputerPartition(request_dict)
def getInformation(self, partition_reference):
if not getattr(self, '_hateoas_navigator', None):
raise Exception('SlapOS Master Hateoas API required for this operation is not availble.')
raw_information = self._hateoas_navigator.getHostingSubscriptionRootSoftwareInstanceInformation(partition_reference)
software_instance = SoftwareInstance()
# XXX redefine SoftwareInstance to be more consistent
for key, value in raw_information.iteritems():
if key in ['_links']:
continue
setattr(software_instance, '_%s' % key, value)
setattr(software_instance, '_software_release_url', raw_information['_links']['software_release'])
return software_instance
def requestComputer(self, computer_reference):
"""
Requests a computer.
"""
xml = self._connection_helper.POST('requestComputer', data={'computer_title': computer_reference})
computer = xml_marshaller.loads(xml)
computer._connection_helper = self._connection_helper
computer._hateoas_navigator = self._hateoas_navigator
return computer
def _syncComputerInformation(func):
"""
Synchronize computer object with server information
"""
def decorated(self, *args, **kw):
if getattr(self, '_synced', 0):
return func(self, *args, **kw)
computer = self._connection_helper.getFullComputerInformation(self._computer_id)
for key, value in computer.__dict__.items():
if isinstance(value, unicode):
# convert unicode to utf-8
setattr(self, key, value.encode('utf-8'))
else:
setattr(self, key, value)
setattr(self, '_synced', True)
for computer_partition in self.getComputerPartitionList():
setattr(computer_partition, '_synced', True)
return func(self, *args, **kw)
return decorated
class Computer(SlapDocument):
zope.interface.implements(interface.IComputer)
def __init__(self, computer_id, connection_helper=None, hateoas_navigator=None):
SlapDocument.__init__(self, connection_helper, hateoas_navigator)
self._computer_id = computer_id
def __getinitargs__(self):
return (self._computer_id, )
@_syncComputerInformation
def getSoftwareReleaseList(self):
"""
Returns the list of software release which has to be supplied by the
computer.
Raise an INotFoundError if computer_guid doesn't exist.
"""
for software_relase in self._software_release_list:
software_relase._connection_helper = self._connection_helper
software_relase._hateoas_navigator = self._hateoas_navigator
return self._software_release_list
@_syncComputerInformation
def getComputerPartitionList(self):
for computer_partition in self._computer_partition_list:
computer_partition._connection_helper = self._connection_helper
computer_partition._hateoas_navigator = self._hateoas_navigator
return [x for x in self._computer_partition_list]
def reportUsage(self, computer_usage):
if computer_usage == "":
return
self._connection_helper.POST('useComputer', data={
'computer_id': self._computer_id,
'use_string': computer_usage})
def updateConfiguration(self, xml):
return self._connection_helper.POST('loadComputerConfigurationFromXML', data={'xml': xml})
def bang(self, message):
self._connection_helper.POST('computerBang', data={
'computer_id': self._computer_id,
'message': message})
def getStatus(self):
xml = self._connection_helper.GET('getComputerStatus', params={'computer_id': self._computer_id})
return xml_marshaller.loads(xml)
def revokeCertificate(self):
self._connection_helper.POST('revokeComputerCertificate', data={
'computer_id': self._computer_id})
def generateCertificate(self):
xml = self._connection_helper.POST('generateComputerCertificate', data={
'computer_id': self._computer_id})
return xml_marshaller.loads(xml)
def parsed_error_message(status, body, path):
m = re.search('(Error Value:\n.*)', body, re.MULTILINE)
if m:
match = ' '.join(line.strip() for line in m.group(0).split('\n'))
return '%s (status %s while calling %s)' % (
saxutils.unescape(match),
status,
path
)
else:
return 'Server responded with wrong code %s with %s' % (status, path)
class ComputerPartition(SlapRequester):
zope.interface.implements(interface.IComputerPartition)
def __init__(self, computer_id=None, partition_id=None,
request_dict=None, connection_helper=None,
hateoas_navigator=None):
SlapDocument.__init__(self, connection_helper, hateoas_navigator)
if request_dict is not None and (computer_id is not None or
partition_id is not None):
raise TypeError('request_dict conflicts with computer_id and '
'partition_id')
if request_dict is None and (computer_id is None or partition_id is None):
raise TypeError('computer_id and partition_id or request_dict are '
'required')
self._computer_id = computer_id
self._partition_id = partition_id
self._request_dict = request_dict
def __getinitargs__(self):
return (self._computer_id, self._partition_id, )
def request(self, software_release, software_type, partition_reference,
shared=False, partition_parameter_kw=None, filter_kw=None,
state=None):
if partition_parameter_kw is None:
partition_parameter_kw = {}
elif not isinstance(partition_parameter_kw, dict):
raise ValueError("Unexpected type of partition_parameter_kw '%s'" %
partition_parameter_kw)
if filter_kw is None:
filter_kw = {}
elif not isinstance(filter_kw, dict):
raise ValueError("Unexpected type of filter_kw '%s'" %
filter_kw)
# Let enforce a default software type
if software_type is None:
software_type = DEFAULT_SOFTWARE_TYPE
request_dict = {
'computer_id': self._computer_id,
'computer_partition_id': self._partition_id,
'software_release': software_release,
'software_type': software_type,
'partition_reference': partition_reference,
'shared_xml': xml_marshaller.dumps(shared),
'partition_parameter_xml': xml_marshaller.dumps(
partition_parameter_kw),
'filter_xml': xml_marshaller.dumps(filter_kw),
'state': xml_marshaller.dumps(state),
}
return self._requestComputerPartition(request_dict)
def building(self):
self._connection_helper.POST('buildingComputerPartition', data={
'computer_id': self._computer_id,
'computer_partition_id': self.getId()})
def available(self):
self._connection_helper.POST('availableComputerPartition', data={
'computer_id': self._computer_id,
'computer_partition_id': self.getId()})
def destroyed(self):
self._connection_helper.POST('destroyedComputerPartition', data={
'computer_id': self._computer_id,
'computer_partition_id': self.getId(),
})
def started(self):
self._connection_helper.POST('startedComputerPartition', data={
'computer_id': self._computer_id,
'computer_partition_id': self.getId(),
})
def stopped(self):
self._connection_helper.POST('stoppedComputerPartition', data={
'computer_id': self._computer_id,
'computer_partition_id': self.getId(),
})
def error(self, error_log, logger=None):
try:
self._connection_helper.POST('softwareInstanceError', data={
'computer_id': self._computer_id,
'computer_partition_id': self.getId(),
'error_log': error_log})
except Exception:
(logger or fallback_logger).exception('')
def bang(self, message):
self._connection_helper.POST('softwareInstanceBang', data={
'computer_id': self._computer_id,
'computer_partition_id': self.getId(),
'message': message})
def rename(self, new_name, slave_reference=None):
post_dict = {
'computer_id': self._computer_id,
'computer_partition_id': self.getId(),
'new_name': new_name,
}
if slave_reference:
post_dict['slave_reference'] = slave_reference
self._connection_helper.POST('softwareInstanceRename', data=post_dict)
def getInformation(self, partition_reference):
"""
Return all needed informations about an existing Computer Partition
in the Instance tree of the current Computer Partition.
"""
if not getattr(self, '_hateoas_navigator', None):
raise Exception('SlapOS Master Hateoas API required for this operation is not availble.')
raw_information = self._hateoas_navigator.getRelatedInstanceInformation(partition_reference)
software_instance = SoftwareInstance()
# XXX redefine SoftwareInstance to be more consistent
for key, value in raw_information.iteritems():
if key in ['_links']:
continue
setattr(software_instance, '_%s' % key, value)
setattr(software_instance, '_software_release_url', raw_information['_links']['software_release'])
return software_instance
def getId(self):
if not getattr(self, '_partition_id', None):
raise ResourceNotReady()
return self._partition_id
def getInstanceGuid(self):
"""Return instance_guid. Raise ResourceNotReady if it doesn't exist."""
if not getattr(self, '_instance_guid', None):
raise ResourceNotReady()
return self._instance_guid
def getState(self):
"""return _requested_state. Raise ResourceNotReady if it doesn't exist."""
if not getattr(self, '_requested_state', None):
raise ResourceNotReady()
return self._requested_state
def getType(self):
"""
return the Software Type of the instance.
Raise RessourceNotReady if not present.
"""
# XXX: software type should not belong to the parameter dict.
software_type = self.getInstanceParameterDict().get(
'slap_software_type', None)
if not software_type:
raise ResourceNotReady()
return software_type
def getInstanceParameterDict(self):
return getattr(self, '_parameter_dict', None) or {}
def getConnectionParameterDict(self):
connection_dict = getattr(self, '_connection_dict', None)
if connection_dict is None:
# XXX Backward compatibility for older slapproxy (<= 1.0.0)
connection_dict = xml2dict(getattr(self, 'connection_xml', ''))
return connection_dict or {}
def getSoftwareRelease(self):
"""
Returns the software release associate to the computer partition.
"""
if not getattr(self, '_software_release_document', None):
raise NotFoundError("No software release information for partition %s" %
self.getId())
else:
return self._software_release_document
def setConnectionDict(self, connection_dict, slave_reference=None):
if self.getConnectionParameterDict() != connection_dict:
self._connection_helper.POST('setComputerPartitionConnectionXml', data={
'computer_id': self._computer_id,
'computer_partition_id': self._partition_id,
'connection_xml': xml_marshaller.dumps(connection_dict),
'slave_reference': slave_reference})
def getInstanceParameter(self, key):
parameter_dict = getattr(self, '_parameter_dict', None) or {}
if key in parameter_dict:
return parameter_dict[key]
else:
raise NotFoundError("%s not found" % key)
def getConnectionParameter(self, key):
connection_dict = self.getConnectionParameterDict()
if key in connection_dict:
return connection_dict[key]
else:
raise NotFoundError("%s not found" % key)
def setUsage(self, usage_log):
# XXX: this implementation has not been reviewed
self.usage = usage_log
def getCertificate(self):
xml = self._connection_helper.GET('getComputerPartitionCertificate',
params={
'computer_id': self._computer_id,
'computer_partition_id': self._partition_id,
}
)
return xml_marshaller.loads(xml)
def getStatus(self):
xml = self._connection_helper.GET('getComputerPartitionStatus',
params={
'computer_id': self._computer_id,
'computer_partition_id': self._partition_id,
}
)
return xml_marshaller.loads(xml)
def _addIpv6Brackets(url):
# if master_url contains an ipv6 without bracket, add it
# Note that this is mostly to limit specific issues with
# backward compatiblity, not to ensure generic detection.
api_scheme, api_netloc, api_path, api_query, api_fragment = urlparse.urlsplit(url)
try:
ip = netaddr.IPAddress(api_netloc)
port = None
except netaddr.AddrFormatError:
try:
ip = netaddr.IPAddress(':'.join(api_netloc.split(':')[:-1]))
port = api_netloc.split(':')[-1]
except netaddr.AddrFormatError:
ip = port = None
if ip and ip.version == 6:
api_netloc = '[%s]' % ip
if port:
api_netloc = '%s:%s' % (api_netloc, port)
url = urlparse.urlunsplit((api_scheme, api_netloc, api_path, api_query, api_fragment))
return url
class ConnectionHelper:
def __init__(self, master_url, key_file=None,
cert_file=None, master_ca_file=None, timeout=None):
master_url = _addIpv6Brackets(master_url)
if master_url.endswith('/'):
self.slapgrid_uri = master_url
else:
# add a slash or the last path segment will be ignored by urljoin
self.slapgrid_uri = master_url + '/'
self.key_file = key_file
self.cert_file = cert_file
self.master_ca_file = master_ca_file
self.timeout = timeout
def getComputerInformation(self, computer_id):
xml = self.GET('getComputerInformation', params={'computer_id': computer_id})
return xml_marshaller.loads(xml)
def getFullComputerInformation(self, computer_id):
"""
Retrieve from SlapOS Master Computer instance containing all needed
informations (Software Releases, Computer Partitions, ...).
"""
path = 'getFullComputerInformation'
params = {'computer_id': computer_id}
if not computer_id:
# XXX-Cedric: should raise something smarter than "NotFound".
raise NotFoundError('%r %r' % (path, params))
try:
xml = self.GET(path, params=params)
except NotFoundError:
# XXX: This is a ugly way to keep backward compatibility,
# We should stablise slap library soon.
xml = self.GET('getComputerInformation', params=params)
if type(xml) is unicode:
xml = str(xml)
xml.encode('utf-8')
return xml_marshaller.loads(xml)
def do_request(self, method, path, params=None, data=None, headers=None):
url = urlparse.urljoin(self.slapgrid_uri, path)
if headers is None:
headers = {}
headers.setdefault('Accept', '*/*')
if path.startswith('/'):
path = path[1:]
# raise ValueError('method path should be relative: %s' % path)
try:
if url.startswith('https'):
cert = (self.cert_file, self.key_file)
else:
cert = None
# XXX TODO: handle host cert verify
# Old behavior was to pass empty parameters as "None" value.
# Behavior kept for compatibility with old slapproxies (< v1.3.3).
# Can be removed when old slapproxies are no longer in use.
if data:
for k, v in data.iteritems():
if v is None:
data[k] = 'None'
req = method(url=url,
params=params,
cert=cert,
verify=False,
data=data,
headers=headers,
timeout=self.timeout)
req.raise_for_status()
except (requests.Timeout, requests.ConnectionError) as exc:
raise ConnectionError("Couldn't connect to the server. Please "
"double check given master-url argument, and make sure that IPv6 is "
"enabled on your machine and that the server is available. The "
"original error was:\n%s" % exc)
except requests.HTTPError as exc:
if exc.response.status_code == requests.status_codes.codes.not_found:
msg = url
if params:
msg += ' - %s' % params
raise NotFoundError(msg)
elif exc.response.status_code == requests.status_codes.codes.request_timeout:
# this is explicitly returned by SlapOS master, and does not really mean timeout
raise ResourceNotReady(path)
# XXX TODO test request timeout and resource not found
else:
# we don't know how or don't want to handle these (including Unauthorized)
req.raise_for_status()
except requests.exceptions.SSLError as exc:
raise AuthenticationError("%s\nCouldn't authenticate computer. Please "
"check that certificate and key exist and are valid." % exc)
# XXX TODO parse server messages for client configure and node register
# elif response.status != httplib.OK:
# message = parsed_error_message(response.status,
# response.read(),
# path)
# raise ServerError(message)
return req
def GET(self, path, params=None, headers=None):
req = self.do_request(requests.get,
path=path,
params=params,
headers=headers)
return req.text.encode('utf-8')
def POST(self, path, params=None, data=None,
content_type='application/x-www-form-urlencoded'):
req = self.do_request(requests.post,
path=path,
params=params,
data=data,
headers={'Content-type': content_type})
return req.text.encode('utf-8')
class slap:
zope.interface.implements(interface.slap)
def initializeConnection(self, slapgrid_uri,
key_file=None, cert_file=None,
master_ca_file=None,
timeout=60,
slapgrid_rest_uri=None):
if master_ca_file:
raise NotImplementedError('Master certificate not verified in this version: %s' % master_ca_file)
self._connection_helper = ConnectionHelper(slapgrid_uri, key_file, cert_file, master_ca_file, timeout)
if not slapgrid_rest_uri:
try:
slapgrid_rest_uri = self._connection_helper.GET('getHateoasUrl')
except:
pass
if slapgrid_rest_uri:
self._hateoas_navigator = SlapHateoasNavigator(
slapgrid_rest_uri,
key_file, cert_file,
master_ca_file, timeout
)
else:
self._hateoas_navigator = None
# XXX-Cedric: this method is never used and thus should be removed.
def registerSoftwareRelease(self, software_release):
"""
Registers connected representation of software release and
returns SoftwareRelease class object
"""
return SoftwareRelease(software_release=software_release,
connection_helper=self._connection_helper,
hateoas_navigator=self._hateoas_navigator
)
def registerComputer(self, computer_guid):
"""
Registers connected representation of computer and
returns Computer class object
"""
return Computer(computer_guid,
connection_helper=self._connection_helper,
hateoas_navigator=self._hateoas_navigator
)
def registerComputerPartition(self, computer_guid, partition_id):
"""
Registers connected representation of computer partition and
returns Computer Partition class object
"""
if not computer_guid or not partition_id:
# XXX-Cedric: should raise something smarter than NotFound
raise NotFoundError
xml = self._connection_helper.GET('registerComputerPartition',
params = {
'computer_reference': computer_guid,
'computer_partition_reference': partition_id,
}
)
if type(xml) is unicode:
xml = str(xml)
xml.encode('utf-8')
result = xml_marshaller.loads(xml)
# XXX: dirty hack to make computer partition usable. xml_marshaller is too
# low-level for our needs here.
result._connection_helper = self._connection_helper
result._hateoas_navigator = self._hateoas_navigator
return result
def registerOpenOrder(self):
return OpenOrder(
connection_helper=self._connection_helper,
hateoas_navigator=self._hateoas_navigator
)
def registerSupply(self):
return Supply(
connection_helper=self._connection_helper,
hateoas_navigator=self._hateoas_navigator
)
def getSoftwareReleaseListFromSoftwareProduct(self,
software_product_reference=None, software_release_url=None):
url = 'getSoftwareReleaseListFromSoftwareProduct'
params = {}
if software_product_reference:
if software_release_url is not None:
raise AttributeError('Both software_product_reference and '
'software_release_url parameters are specified.')
params['software_product_reference'] = software_product_reference
else:
if software_release_url is None:
raise AttributeError('None of software_product_reference and '
'software_release_url parameters are specified.')
params['software_release_url'] = software_release_url
xml = self._connection_helper.GET(url, params=params)
if type(xml) is unicode:
xml = str(xml)
xml.encode('utf-8')
result = xml_marshaller.loads(xml)
assert(type(result) == list)
return result
def getOpenOrderDict(self):
if not getattr(self, '_hateoas_navigator', None):
raise Exception('SlapOS Master Hateoas API required for this operation is not availble.')
return self._hateoas_navigator.getHostingSubscriptionDict()
class HateoasNavigator(object):
"""
Navigator for HATEOAS-style APIs.
Inspired by
https://git.erp5.org/gitweb/jio.git/blob/HEAD:/src/jio.storage/erp5storage.js
"""
# XXX: needs to be designed for real. For now, just a non-maintainable prototype.
# XXX: export to a standalone library, independant from slap.
def __init__(self, slapgrid_uri,
key_file=None, cert_file=None,
master_ca_file=None, timeout=60):
self.slapos_master_hateoas_uri = slapgrid_uri
self.key_file = key_file
self.cert_file = cert_file
self.master_ca_file = master_ca_file
self.timeout = timeout
def GET(self, uri, headers=None):
connection_helper = ConnectionHelper(
uri, self.key_file, self.cert_file, self.master_ca_file, self.timeout)
return connection_helper.GET(uri, headers=headers)
def hateoasGetLinkFromLinks(self, links, title):
if type(links) == dict:
if links.get('title') == title:
return links['href']
raise NotFoundError('Action %s not found.' % title)
for action in links:
if action.get('title') == title:
return action['href']
else:
raise NotFoundError('Action %s not found.' % title)
def getRelativeUrlFromUrn(self, urn):
urn_schema = 'urn:jio:get:'
try:
_, url = urn.split(urn_schema)
except ValueError:
return
return str(url)
def getSiteDocument(self, url, headers=None):
result = self.GET(url, headers)
return json.loads(result)
def getRootDocument(self):
# XXX what about cache?
cached_root_document = getattr(self, 'root_document', None)
if cached_root_document:
return cached_root_document
self.root_document = self.getSiteDocument(
self.slapos_master_hateoas_uri,
headers={'Cache-Control': 'no-cache'}
)
return self.root_document
def getDocumentAndHateoas(self, relative_url, view='view'):
site_document = self.getRootDocument()
return expand(
site_document['_links']['traverse']['href'],
dict(relative_url=relative_url, view=view)
)
def getMeDocument(self):
person_relative_url = self.getRelativeUrlFromUrn(
self.getRootDocument()['_links']['me']['href'])
person_url = self.getDocumentAndHateoas(person_relative_url)
return json.loads(self.GET(person_url))
class SlapHateoasNavigator(HateoasNavigator):
def _hateoas_getHostingSubscriptionDict(self):
action_object_slap_list = self.getMeDocument()['_links']['action_object_slap']
for action in action_object_slap_list:
if action.get('title') == 'getHateoasHostingSubscriptionList':
getter_link = action['href']
break
else:
raise Exception('Hosting subscription not found.')
result = self.GET(getter_link)
return json.loads(result)['_links']['content']
# XXX rename me to blablaUrl(self)
def _hateoas_getRelatedHostingSubscription(self):
action_object_slap_list = self.getMeDocument()['_links']['action_object_slap']
getter_link = self.hateoasGetLinkFromLinks(action_object_slap_list, 'getHateoasRelatedHostingSubscription')
result = self.GET(getter_link)
return json.loads(result)['_links']['action_object_jump']['href']
def _hateoasGetInformation(self, url):
result = self.GET(url)
result = json.loads(result)
object_link = self.hateoasGetLinkFromLinks(
result['_links']['action_object_slap'],
'getHateoasInformation'
)
result = self.GET(object_link)
return json.loads(result)
def getHateoasInstanceList(self, hosting_subscription_url):
hosting_subscription = json.loads(self.GET(hosting_subscription_url))
instance_list_url = self.hateoasGetLinkFromLinks(hosting_subscription['_links']['action_object_slap'], 'getHateoasInstanceList')
instance_list = json.loads(self.GET(instance_list_url))
return instance_list['_links']['content']
def getHostingSubscriptionDict(self):
hosting_subscription_link_list = self._hateoas_getHostingSubscriptionDict()
hosting_subscription_dict = {}
for hosting_subscription_link in hosting_subscription_link_list:
raw_information = self.getHostingSubscriptionRootSoftwareInstanceInformation(hosting_subscription_link['title'])
software_instance = SoftwareInstance()
# XXX redefine SoftwareInstance to be more consistent
for key, value in raw_information.iteritems():
if key in ['_links']:
continue
setattr(software_instance, '_%s' % key, value)
setattr(software_instance, '_software_release_url', raw_information['_links']['software_release'])
hosting_subscription_dict[software_instance._title] = software_instance
return hosting_subscription_dict
def getHostingSubscriptionRootSoftwareInstanceInformation(self, reference):
hosting_subscription_list = self._hateoas_getHostingSubscriptionDict()
for hosting_subscription in hosting_subscription_list:
if hosting_subscription.get('title') == reference:
hosting_subscription_url = hosting_subscription['href']
break
else:
raise NotFoundError('This document does not exist.')
hosting_subscription = json.loads(self.GET(hosting_subscription_url))
software_instance_url = self.hateoasGetLinkFromLinks(
hosting_subscription['_links']['action_object_slap'],
'getHateoasRootInstance'
)
response = self.GET(software_instance_url)
response = json.loads(response)
software_instance_url = response['_links']['content'][0]['href']
return self._hateoasGetInformation(software_instance_url)
def getRelatedInstanceInformation(self, reference):
related_hosting_subscription_url = self._hateoas_getRelatedHostingSubscription()
instance_list = self.getHateoasInstanceList(related_hosting_subscription_url)
instance_url = self.hateoasGetLinkFromLinks(instance_list, reference)
instance = self._hateoasGetInformation(instance_url)
return instance
slapos.core-1.3.10/slapos/slap/interface/ 0000755 0000000 0000000 00000000000 12517721227 016730 5 ustar root root slapos.core-1.3.10/slapos/slap/interface/__init__.py 0000644 0000000 0000000 00000002500 12517720512 021032 0 ustar root root ##############################################################################
#
# Copyright (c) 2010, 2011, 2012 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
slapos.core-1.3.10/slapos/slap/interface/slap.py 0000644 0000000 0000000 00000034265 12517720512 020247 0 ustar root root ##############################################################################
#
# Copyright (c) 2010, 2011, 2012 Vifib SARL and Contributors.
# All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public License
# as published by the Free Software Foundation; either version 2.1
# of the License, or (at your option) any later version.
#
# 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 Lesser General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from zope.interface import Interface
"""
Note: all strings accepted/returned by the slap library are encoded in UTF-8.
"""
class IException(Interface):
"""
Classes which implement IException are used to report errors.
"""
class IConnectionError(IException):
"""
Classes which implement IServerError are used to report a connection problem
to the slap server.
"""
class IServerError(IException):
"""
Classes which implement IServerError are used to report unexpected error
from the slap server.
"""
class INotFoundError(IException):
"""
Classes which implement INotFoundError are used to report missing
informations on the slap server.
"""
class IResourceNotReady(IException):
"""
Classes which implement IResourceNotReady are used to report resource not
ready on the slap server.
"""
class IRequester(Interface):
"""
Classes which implement IRequester can request software instance to the
slapgrid server.
"""
def request(software_release, software_type, partition_reference,
shared=False, partition_parameter_kw=None, filter_kw=None):
"""
Request software release instantiation to slapgrid server.
Returns a new computer partition document, where this sofware release will
be installed.
software_release -- uri of the software release
which has to be instanciated
software_type -- type of component provided by software_release
partition_reference -- local reference of the instance used by the recipe
to identify the instances.
shared -- boolean to use a shared service
partition_parameter_kw -- dictionary of parameter used to fill the
parameter dict of newly created partition.
filter_kw -- dictionary of filtering parameter to select the requested
computer partition.
computer_guid - computer of the requested partition
partition_type - virtio, slave, full, limited
port - port provided by the requested partition
Example:
request('http://example.com/toto/titi', 'typeA', 'mysql_1')
"""
def getInformation(partition_reference):
"""
Get informations about an existing instance.
If it is called from a Computer Partition, get informations
about Software Instance of the instance tree.
partition_reference -- local reference of the instance used by the recipe
to identify the instances.
"""
class IBuildoutController(Interface):
"""
Classes which implement IBuildoutController can report the buildout run
status to the slapgrid server.
"""
def available():
"""
Notify (to the slapgrid server) that the software instance is
available.
"""
def building():
"""
Notify (to the slapgrid server) that the buildout is not
available and under creation.
"""
def error(error_log):
"""
Notify (to the slapgrid server) that the buildout is not available
and reports an error.
error_log -- a text describing the error
It can be a traceback for example.
"""
class ISoftwareRelease(IBuildoutController):
"""
Software release interface specification
"""
def getURI():
"""
Returns a string representing the uri of the software release.
"""
def getComputerId():
"""
Returns a string representing the identifier of the computer where the SR
is installed.
"""
def getState():
"""
Returns a string representing the expected state of the software
installation.
The result can be: available, destroyed
"""
def destroyed():
"""
Notify (to the slapgrid server) that the software installation has
been correctly destroyed.
"""
class ISoftwareProductCollection(Interface):
"""
Fake object representing the abstract of all Software Products.
Can be used to call "Product().mysoftwareproduct", or, simpler,
"product.mysoftwareproduct", to get the best Software Release URL of the
Software Product "mysoftwareproduct".
Example: product.kvm will have the value of the latest Software
Release URL of KVM.
"""
class ISoftwareInstance(Interface):
"""
Classes which implement ISoftwareRelease are used by slap to represent
informations about a Software Instance.
"""
class IComputerPartition(IBuildoutController, IRequester):
"""
Computer Partition interface specification
Classes which implement IComputerPartition can propagate the computer
partition state to the SLAPGRID server and request new computer partition
creation.
"""
def stopped():
"""
Notify (to the slapgrid server) that the software instance is
available and stopped.
"""
def started():
"""
Notify (to the slapgrid server) that the software instance is
available and started.
"""
def destroyed():
"""
Notify (to the slapgrid server) that the software instance has
been correctly destroyed.
"""
def getId():
"""
Returns a string representing the identifier of the computer partition
inside the slapgrid server.
"""
def getInstanceGuid():
"""
Returns a string representing the unique identifier of the instance
inside the slapgrid server.
"""
def getState():
"""
Returns a string representing the expected state of the computer partition.
The result can be: started, stopped, destroyed
"""
def getSoftwareRelease():
"""
Returns the software release associate to the computer partition.
Raise an INotFoundError if no software release is associated.
"""
def getInstanceParameterDict():
"""
Returns a dictionary of instance parameters.
The contained values can be used to fill the software instanciation
profile.
"""
def getConnectionParameterDict():
"""
Returns a dictionary of connection parameters.
The contained values are connection parameters of a compute partition.
"""
def getType():
"""
Returns the Software Type of the instance.
"""
def setUsage(usage_log):
"""
Associate a usage log to the computer partition.
This method does not report the usage to the slapgrid server. See
IComputer.report.
usage_log -- a text describing the computer partition usage.
It can be an XML for example.
"""
def bang(log):
"""
Report a problem detected on a computer partition.
This will trigger the reinstanciation of all partitions in the instance tree.
log -- a text explaining why the method was called
"""
def getCertificate():
"""
Returns a dictionnary containing the authentification certificates
associated to the computer partition.
The dictionnary keys are:
key -- value is a SSL key
certificate -- value is a SSL certificate
Raise an INotFoundError if no software release is associated.
"""
def setConnectionDict(connection_dict, slave_reference=None):
"""
Store the connection parameters associated to a partition.
connection_dict -- dictionary of parameter used to fill the
connection dict of the partition.
slave_reference -- current reference of the slave instance to modify
"""
def getInstanceParameter(key):
"""
Returns the instance parameter associated to the key.
Raise an INotFoundError if no key is defined.
key -- a string name of the parameter
"""
def getConnectionParameter(key):
"""
Return the connection parameter associate to the key.
Raise an INotFoundError if no key is defined.
key -- a string name of the parameter
"""
def rename(partition_reference, slave_reference=None):
"""
Change the partition reference of a partition
partition_reference -- new local reference of the instance used by the recipe
to identify the instances.
slave_reference -- current reference of the slave instance to modify
"""
def getStatus():
"""
Returns a dictionnary containing the latest status of the
computer partition.
The dictionnary keys are:
user -- user who reported the latest status
created_at -- date of the status
text -- message log of the status
"""
class IComputer(Interface):
"""
Computer interface specification
Classes which implement IComputer can fetch informations from the slapgrid
server to know which Software Releases and Software Instances have to be
installed.
"""
def getSoftwareReleaseList():
"""
Returns the list of software release which has to be supplied by the
computer.
Raise an INotFoundError if computer_guid doesn't exist.
"""
def getComputerPartitionList():
"""
Returns the list of configured computer partitions associated to this
computer.
Raise an INotFoundError if computer_guid doesn't exist.
"""
def reportUsage(computer_partition_list):
"""
Report the computer usage to the slapgrid server.
IComputerPartition.setUsage has to be called on each computer partition to
define each usage.
computer_partition_list -- a list of computer partition for which the usage
needs to be reported.
"""
def bang(log):
"""
Report a problem detected on a computer.
This will trigger IComputerPartition.bang on all instances hosted by the
Computer.
log -- a text explaining why the method was called
"""
def updateConfiguration(configuration_xml):
"""
Report the current computer configuration.
configuration_xml -- computer XML description generated by slapformat
"""
def getStatus():
"""
Returns a dictionnary containing the latest status of the
computer.
The dictionnary keys are:
user -- user who reported the latest status
created_at -- date of the status
text -- message log of the status
"""
def generateCertificate():
"""
Returns a dictionnary containing the new certificate files for
the computer.
The dictionnary keys are:
key -- key file
certificate -- certificate file
Raise ValueError is another certificate is already valid.
"""
def revokeCertificate():
"""
Revoke current computer certificate.
Raise ValueError is there is not valid certificate.
"""
class IOpenOrder(IRequester):
"""
Open Order interface specification
Classes which implement Open Order describe which kind of software instances
is requested by a given client.
"""
def requestComputer(computer_reference):
"""
Request a computer to slapgrid server.
Returns a new computer document.
computer_reference -- local reference of the computer
"""
class ISupply(Interface):
"""
Supply interface specification
Classes which implement Supply describe which kind of software releases
a given client is ready to supply.
"""
def supply(software_release, computer_guid=None):
"""
Tell that given client is ready to supply given sofware release
software_release -- uri of the software release
which has to be instanciated
computer_guid -- the identifier of the computer inside the slapgrid
server.
"""
class slap(Interface):
"""
Initialise slap connection to the slapgrid server
Slapgrid server URL is defined during the slap library installation,
as recipes should not use another server.
"""
def initializeConnection(slapgrid_uri, authentification_key=None):
"""
Initialize the connection parameters to the slapgrid servers.
slapgrid_uri -- uri the slapgrid server connector
authentification_key -- string the authentificate the agent.
Example: https://slapos.server/slap_interface
"""
def registerComputer(computer_guid):
"""
Instanciate a computer in the slap library.
computer_guid -- the identifier of the computer inside the slapgrid server.
"""
def registerComputerPartition(computer_guid, partition_id):
"""
Instanciate a computer partition in the slap library.
computer_guid -- the identifier of the computer inside the slapgrid server.
partition_id -- the identifier of the computer partition inside the
slapgrid server.
Raise an INotFoundError if computer_guid doesn't exist.
"""
def registerSoftwareRelease(software_release):
"""
Instanciate a software release in the slap library.
software_release -- uri of the software release definition
"""
def registerOpenOrder():
"""
Instanciate an open order in the slap library.
"""
def registerSupply():
"""
Instanciate a supply in the slap library.
"""
def getSoftwareReleaseListFromSoftwareProduct(software_product_reference, software_release_url):
"""
Get the list of Software Releases from a product or from another related
Sofware Release, from a Software Product point of view.
"""
def getOpenOrderDict():
"""
Get the list of existing open orders (services) for the current user.
"""
slapos.core-1.3.10/slapos/slap/doc/ 0000755 0000000 0000000 00000000000 12517721227 015535 5 ustar root root slapos.core-1.3.10/slapos/slap/doc/computer_consumption.xsd 0000644 0000000 0000000 00000004220 12517720512 022543 0 ustar root root