Browse Source

Initial import

Andrés J. Díaz 6 years ago
parent
commit
76d1a91a81
73 changed files with 6537 additions and 0 deletions
  1. 63 0
      CONTRIBUTING
  2. 339 0
      LICENSE
  3. 69 0
      README.rst
  4. 153 0
      doc/Makefile
  5. BIN
      doc/source/_static/logo.png
  6. BIN
      doc/source/_static/mico.jpg
  7. 18 0
      doc/source/_themes/aj/layout.html
  8. 19 0
      doc/source/_themes/aj/relations.html
  9. 30 0
      doc/source/_themes/aj/sidebar.html
  10. 578 0
      doc/source/_themes/aj/static/flasky.css_t
  11. 19 0
      doc/source/_themes/aj/theme.conf
  12. 86 0
      doc/source/_themes/flask_theme_support.py
  13. 9 0
      doc/source/api.rst
  14. 246 0
      doc/source/conf.py
  15. 63 0
      doc/source/contributing.rst
  16. 8 0
      doc/source/decorators.rst
  17. 24 0
      doc/source/environ.rst
  18. 100 0
      doc/source/index.rst
  19. 54 0
      doc/source/mico.lib.aws.ec2.rst
  20. 18 0
      doc/source/mico.lib.aws.rst
  21. 21 0
      doc/source/mico.lib.core.package.rst
  22. 90 0
      doc/source/mico.lib.core.rst
  23. 5 0
      doc/source/mico.lib.rst
  24. 63 0
      doc/source/templates.rst
  25. 144 0
      examples/complex.py
  26. 30 0
      examples/data/sudoers.tpl
  27. 53 0
      examples/ec2.py
  28. 22 0
      examples/simple.py
  29. 31 0
      examples/stupid.py
  30. 118 0
      mico/__init__.py
  31. 93 0
      mico/decorators.py
  32. 126 0
      mico/environ.py
  33. 37 0
      mico/fifo.py
  34. 143 0
      mico/hook.py
  35. 0 0
      mico/lib/__init__.py
  36. 13 0
      mico/lib/aws/__init__.py
  37. 351 0
      mico/lib/aws/ec2/__init__.py
  38. 411 0
      mico/lib/aws/ec2/autoscale.py
  39. 122 0
      mico/lib/aws/ec2/cw.py
  40. 127 0
      mico/lib/aws/ec2/ebs.py
  41. 95 0
      mico/lib/aws/ec2/eip.py
  42. 141 0
      mico/lib/aws/ec2/elb.py
  43. 134 0
      mico/lib/aws/ec2/sg.py
  44. 72 0
      mico/lib/aws/iam.py
  45. 200 0
      mico/lib/aws/r53.py
  46. 19 0
      mico/lib/core/__init__.py
  47. 50 0
      mico/lib/core/dir.py
  48. 292 0
      mico/lib/core/file.py
  49. 99 0
      mico/lib/core/group.py
  50. 79 0
      mico/lib/core/local.py
  51. 49 0
      mico/lib/core/network.py
  52. 82 0
      mico/lib/core/package/__init__.py
  53. 108 0
      mico/lib/core/package/apt.py
  54. 67 0
      mico/lib/core/package/yum.py
  55. 91 0
      mico/lib/core/service.py
  56. 72 0
      mico/lib/core/ssh.py
  57. 42 0
      mico/lib/core/sudo.py
  58. 28 0
      mico/lib/core/ulimit.py
  59. 102 0
      mico/lib/core/user.py
  60. 8 0
      mico/lib/core/virtualenv.py
  61. 114 0
      mico/output.py
  62. 0 0
      mico/scripts/__init__.py
  63. 223 0
      mico/scripts/cmdline.py
  64. 57 0
      mico/template.py
  65. 0 0
      mico/templates/__init__.py
  66. 99 0
      mico/templates/ls.py
  67. 0 0
      mico/util/__init__.py
  68. 80 0
      mico/util/dicts.py
  69. 32 0
      mico/util/mutex.py
  70. 94 0
      mico/util/storage.py
  71. 35 0
      mico/util/switch.py
  72. 6 0
      requirements.txt
  73. 71 0
      setup.py

+ 63 - 0
CONTRIBUTING

@@ -0,0 +1,63 @@
+============
+Contributing
+============
+
+For more information, please see the official contribution docs at
+http://mico.ajdiaz.me/en/latest/contributing.html.
+
+
+Contributing Code
+=================
+
+* A good patch:
+
+  * is clear.
+  * works across all supported versions of Python.
+  * follows the existing style of the code base (PEP-8).
+  * has comments included as needed.
+
+* A test case that demonstrates the previous flaw that now passes
+  with the included patch.
+* If it adds/changes a public API, it must also include documentation
+  for those changes.
+* Must be appropriately licensed (GPLv2 or newer version).
+
+
+Reporting An Issue/Feature
+==========================
+
+* Check to see if there's an existing issue/pull request for the
+  bug/feature. All issues are at https://github.com/ajdiaz/mico/issues
+  and pull reqs are at https://github.com/ajdiaz/mico/pulls.
+* If there isn't an existing issue there, please file an issue. The ideal
+  report includes:
+
+  * A description of the problem/suggestion.
+  * How to recreate the bug.
+  * If relevant, including the versions of your:
+
+    * Python interpreter
+    * mico
+    * Optionally of the other dependencies involved
+
+  * If possile, create a pull request with a (failing) test case demonstrating
+    what's wrong. This makes the process for fixing bugs quicker & gets issues
+    resolved sooner.
+
+What we need?
+=============
+
+* To improve mico we need a lot of *new libraries*, core libraries is our main
+  focus right now, for next versions of mic we like to have a lot of core
+  libs, such like change parameters in the OS, modify some base system
+  config, or more high level ones like "Install a full LAMP architecture".
+  Every new library is welcome.
+
+* We need more pre-defined templates, like "ls" does. For example a cost
+  template to calculate EC2 budget.
+
+* More EC2 libraries, for example for S3.
+
+* Fix bugs... we need to remove the Beta tag ;)
+
+

+ 339 - 0
LICENSE

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

+ 69 - 0
README.rst

@@ -0,0 +1,69 @@
+===========================================
+Mico: a monkey driven cloud management tool
+===========================================
+
+Mico is a tool-toy to manage a number of hosts deployed in cloud services
+(currently only support Amazon AWS), and also allows you to deploy new hosts
+with specified template or create autoscaling groups and manage them easily.
+
+Installation
+------------
+As usual, mico is available from pypi_, and can be installed using ``pip``
+or ``easy_install``::
+
+  pip install mico
+
+Or::
+
+  easy_install mico
+
+.. _pypi: http://pypi.python.org/pypi
+
+Configure Mico
+--------------
+
+Mico just need an AWS key ID and AWS secret key to run. By default mico just
+take this variables from the OS environment::
+
+    export AWS_ACCESS_KEY_ID="*foo*"
+    export AWS_SECRET_ACCESS_KEY="*bar*"
+
+Creating templates
+------------------
+
+Mico works using the concept of template. A template is just a python code
+(with steroids which we call *libraries*), the template can implements
+a number of functions. Here are a simple and stupid template (``stupid.py``)
+
+.. code:: python
+
+    def hello():
+      print "Hello world!"
+
+    def bye():
+      print "Bye cruel world!"
+
+    def hola(args):
+      print "Hola %s" % args
+
+
+Once, your template is created, you need to put it into a mico template path
+(by default uses ``/etc/mico`` and ``~/.config/mico/``, and the current
+working directory.
+
+Then you can just run mico::
+
+    mico -H my_new_host stupid:hello
+    Hello world!
+
+    mico -H my_new_host stupid:bye
+    Bye cruel world!
+
+    mico stupid:hola everyone
+    Hola everyone
+
+You can see more complex (and useful!) templates in `examples directory`_.
+
+.. _`examples directory`: tree/master/examples
+
+

+ 153 - 0
doc/Makefile

@@ -0,0 +1,153 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/mico.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/mico.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/mico"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/mico"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."

BIN
doc/source/_static/logo.png


BIN
doc/source/_static/mico.jpg


+ 18 - 0
doc/source/_themes/aj/layout.html

@@ -0,0 +1,18 @@
+{%- extends "basic/layout.html" %}
+{%- block extrahead %}
+  {{ super() }}
+  {% if theme_touch_icon %}
+    <link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
+  {% endif %}
+  <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9">
+{% endblock %}
+{%- block relbar2 %}{% endblock %}
+{%- block footer %}
+    <div class="footer">
+      &copy; Copyright {{ copyright }}.
+    </div>
+	<a href="{{ theme_repo_url }}" class="github">
+        <img style="position: absolute; top: 0; right: 0; border: 0;" src="http://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"  class="github"/>
+    </a>
+
+{%- endblock %}

+ 19 - 0
doc/source/_themes/aj/relations.html

@@ -0,0 +1,19 @@
+<h3>Related Topics</h3>
+<ul>
+  <li><a href="{{ pathto(master_doc) }}">Documentation overview</a><ul>
+  {%- for parent in parents %}
+  <li><a href="{{ parent.link|e }}">{{ parent.title }}</a><ul>
+  {%- endfor %}
+    {%- if prev %}
+      <li>Previous: <a href="{{ prev.link|e }}" title="{{ _('previous chapter')
+        }}">{{ prev.title }}</a></li>
+    {%- endif %}
+    {%- if next %}
+      <li>Next: <a href="{{ next.link|e }}" title="{{ _('next chapter')
+        }}">{{ next.title }}</a></li>
+    {%- endif %}
+  {%- for parent in parents %}
+  </ul></li>
+  {%- endfor %}
+  </ul></li>
+</ul>

+ 30 - 0
doc/source/_themes/aj/sidebar.html

@@ -0,0 +1,30 @@
+<p class="logo">
+<a href="/">
+	<img class="logo" src="{{ theme_logo_path }}" title="{{ theme_logo_title }}" />
+</a>
+<p><iframe
+	src="http://ghbtns.com/github-btn.html?user={{ theme_repo_author }}&amp;repo={{ theme_repo_name }}&amp;type=watch&amp;count=true&amp;size=large" 
+	allowtransparency="true" frameborder="0" scrolling="0" width="200px"
+	height="35px"></iframe>
+</p>
+<p class="desc">
+{{ theme_description }}
+</p>
+<h3>Feedback</h3>
+<p>Feedback is greatly appreciated. If you have any questions, comments, random
+praise, or anonymous threats, <a href="mailto:{{ theme_repo_email }}">shoot
+	me an email.</a></p>
+
+<button class="tpl"><a href="https://github.com/ajdiaz/mico/issues">Report bug</a></button>
+
+<h3>Useful Links</h3>
+<ul>
+	{% if theme_package_url %}
+	<li><a href="{{theme_package_url}}">{{theme_repo_name}} @ PyPI</a></li>
+	{% endif %}
+	{% if theme_repo_url %}
+	<li><a href="{{theme_repo_url}}">{{theme_repo_name}} @ github.com</a></li>
+	<li><a href="{{theme_repo_url}}/issues">Issue tracker</a></li>
+	{% endif %}
+</ul>
+

+ 578 - 0
doc/source/_themes/aj/static/flasky.css_t

@@ -0,0 +1,578 @@
+/*
+ * flasky.css_t
+ * ~~~~~~~~~~~~
+ *
+ * :copyright: Copyright 2010 by Armin Ronacher. Modifications by Kenneth Reitz.
+ * :license: Flask Design License, see LICENSE for details.
+ */
+
+{% set page_width = '940px' %}
+{% set sidebar_width = '220px' %}
+
+@import url("basic.css");
+@import url(http://fonts.googleapis.com/css?family=Quattrocento:400,700);
+
+/* -- page layout ----------------------------------------------------------- */
+
+body {
+    font-family: Garamond, Georgia, serif;
+    font-size: 17px;
+    background-color: white;
+    color: #000;
+    margin: 0;
+    padding: 0;
+}
+
+div.document {
+    width: {{ page_width }};
+    margin: 30px auto 0 auto;
+}
+
+div.documentwrapper {
+    float: left;
+    width: 100%;
+}
+
+div.bodywrapper {
+    margin: 0 0 0 {{ sidebar_width }};
+}
+
+div.sphinxsidebar {
+    width: {{ sidebar_width }};
+}
+
+hr {
+    border: 1px solid #B1B4B6;
+}
+
+div.body {
+    background-color: #ffffff;
+    color: #333;
+    padding: 0 30px 0 30px;
+}
+
+img.floatingflask {
+    padding: 0 0 10px 10px;
+    float: right;
+}
+
+div.footer {
+    width: {{ page_width }};
+    margin: 20px auto 30px auto;
+    font-size: 14px;
+    color: #888;
+    text-align: right;
+}
+
+div.footer a {
+    color: #888;
+}
+
+div.related {
+    display: none;
+}
+
+div.sphinxsidebar a {
+    color: #444;
+    text-decoration: none;
+    border-bottom: 1px dotted #999;
+}
+
+div.sphinxsidebar a:hover {
+    border-bottom: 1px solid #999;
+}
+
+div.sphinxsidebar {
+    font-size: 14px;
+    line-height: 1.5;
+}
+
+div.sphinxsidebarwrapper {
+    padding: 18px 10px;
+}
+
+div.sphinxsidebarwrapper p.logo {
+    padding: 0;
+    margin: -10px 0 0 -20px;
+    text-align: center;
+}
+
+div.sphinxsidebar h3,
+div.sphinxsidebar h4 {
+    font-family: 'Garamond', 'Georgia', serif;
+    color: #444;
+    font-size: 24px;
+    font-weight: normal;
+    margin: 0 0 5px 0;
+    padding: 0;
+}
+
+div.sphinxsidebar h4 {
+    font-size: 20px;
+}
+
+div.sphinxsidebar h3 a {
+    color: #444;
+}
+
+div.sphinxsidebar p.logo a,
+div.sphinxsidebar h3 a,
+div.sphinxsidebar p.logo a:hover,
+div.sphinxsidebar h3 a:hover {
+    border: none;
+}
+
+div.sphinxsidebar p {
+    color: #555;
+    margin: 10px 0;
+}
+
+div.sphinxsidebar ul {
+    margin: 10px 0;
+    padding: 0;
+    color: #000;
+}
+
+div.sphinxsidebar input {
+    border: 1px solid #ccc;
+    font-family: 'Georgia', serif;
+    font-size: 1em;
+}
+
+/* -- body styles ----------------------------------------------------------- */
+
+a {
+    color: #004B6B;
+    text-decoration: underline;
+}
+
+a:hover {
+    color: #6D4100;
+    text-decoration: underline;
+}
+
+div.body h1,
+div.body h2,
+div.body h3,
+div.body h4,
+div.body h5,
+div.body h6 {
+    font-family: 'Quattrocento',serif;
+    font-weight: normal;
+    margin: 30px 0px 10px 0px;
+    padding: 0;
+}
+
+p.desc { text-align: justify; }
+
+div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
+div.body h2 { font-size: 180%; }
+div.body h3 { font-size: 150%; }
+div.body h4 { font-size: 130%; }
+div.body h5 { font-size: 100%; }
+div.body h6 { font-size: 100%; }
+
+a.headerlink {
+    color: #ddd;
+    padding: 0 4px;
+    text-decoration: none;
+}
+
+a.headerlink:hover {
+    color: #444;
+    background: #eaeaea;
+}
+
+div.body p, div.body dd, div.body li {
+    line-height: 1.4em;
+}
+
+div.admonition {
+    background: #fafafa;
+    margin: 20px -30px;
+    padding: 10px 30px;
+    border-top: 1px solid #ccc;
+    border-bottom: 1px solid #ccc;
+}
+
+div.admonition tt.xref, div.admonition a tt {
+    border-bottom: 1px solid #fafafa;
+}
+
+dd div.admonition {
+    margin-left: -60px;
+    padding-left: 60px;
+}
+
+div.admonition p.admonition-title {
+    font-family: 'Garamond', 'Georgia', serif;
+    font-weight: normal;
+    font-size: 24px;
+    margin: 0 0 10px 0;
+    padding: 0;
+    line-height: 1;
+}
+
+div.admonition p.last {
+    margin-bottom: 0;
+}
+
+div.highlight {
+    background-color: white;
+}
+
+dt:target, .highlight {
+    background: #FAF3E8;
+}
+
+div.note {
+    background-color: #eee;
+    border: 1px solid #ccc;
+}
+
+div.seealso {
+    background-color: #ffc;
+    border: 1px solid #ff6;
+}
+
+div.topic {
+    background-color: #eee;
+}
+
+p.admonition-title {
+    display: inline;
+}
+
+p.admonition-title:after {
+    content: ":";
+}
+
+pre, tt {
+    font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
+    font-size: 0.9em;
+}
+
+img.screenshot {
+}
+
+tt.descname, tt.descclassname {
+    font-size: 0.95em;
+}
+
+tt.descname {
+    padding-right: 0.08em;
+}
+
+img.screenshot {
+    -moz-box-shadow: 2px 2px 4px #eee;
+    -webkit-box-shadow: 2px 2px 4px #eee;
+    box-shadow: 2px 2px 4px #eee;
+}
+
+table.docutils {
+    border: 1px solid #888;
+    -moz-box-shadow: 2px 2px 4px #eee;
+    -webkit-box-shadow: 2px 2px 4px #eee;
+    box-shadow: 2px 2px 4px #eee;
+}
+
+table.docutils td, table.docutils th {
+    border: 1px solid #888;
+    padding: 0.25em 0.7em;
+}
+
+table.field-list, table.footnote {
+    border: none;
+    -moz-box-shadow: none;
+    -webkit-box-shadow: none;
+    box-shadow: none;
+}
+
+table.footnote {
+    margin: 15px 0;
+    width: 100%;
+    border: 1px solid #eee;
+    background: #fdfdfd;
+    font-size: 0.9em;
+}
+
+table.footnote + table.footnote {
+    margin-top: -15px;
+    border-top: none;
+}
+
+table.field-list th {
+    padding: 0 0.8em 0 0;
+}
+
+table.field-list td {
+    padding: 0;
+}
+
+table.footnote td.label {
+    width: 0px;
+    padding: 0.3em 0 0.3em 0.5em;
+}
+
+table.footnote td {
+    padding: 0.3em 0.5em;
+}
+
+dl {
+    margin: 0;
+    padding: 0;
+}
+
+dl dd {
+    margin-left: 30px;
+}
+
+blockquote {
+    margin: 0 0 0 30px;
+    padding: 0;
+}
+
+ul, ol {
+    margin: 10px 0 10px 30px;
+    padding: 0;
+}
+
+.pre {
+	font-size: 70%;
+}
+
+pre {
+    background: #eee;
+    padding: 10px;
+    margin: 15px 0px;
+    line-height: 1.3em;
+    border-radius:5px;
+    -moz-border-radius:5px;
+    -webkit-border-radius:5px;
+    font-size:70%;
+
+}
+
+dl pre, blockquote pre, li pre {
+    padding: 10px;
+    margin: 15px 0px;
+}
+
+dl dl pre {
+    margin-left: -90px;
+    padding-left: 90px;
+}
+
+tt {
+    background-color: #ecf0f3;
+    color: #222;
+    /* padding: 1px 2px; */
+}
+
+tt.xref, a tt {
+    background-color: #FBFBFB;
+    border-bottom: 1px solid white;
+}
+
+a.reference {
+    text-decoration: none;
+    border-bottom: 1px dotted #004B6B;
+}
+
+a.reference:hover {
+    border-bottom: 1px solid #6D4100;
+}
+
+a.footnote-reference {
+    text-decoration: none;
+    font-size: 0.7em;
+    vertical-align: top;
+    border-bottom: 1px dotted #004B6B;
+}
+
+a.footnote-reference:hover {
+    border-bottom: 1px solid #6D4100;
+}
+
+a:hover tt {
+    background: #EEE;
+}
+
+
+@media screen and (max-width: 870px) {
+
+    div.sphinxsidebar {
+    	display: none;
+    }
+
+    div.document {
+       width: 100%;
+
+    }
+
+    div.documentwrapper {
+    	margin-left: 0;
+    	margin-top: 0;
+    	margin-right: 0;
+    	margin-bottom: 0;
+    }
+
+    div.bodywrapper {
+    	margin-top: 0;
+    	margin-right: 0;
+    	margin-bottom: 0;
+    	margin-left: 0;
+    }
+
+    ul {
+    	margin-left: 0;
+    }
+
+    .document {
+    	width: auto;
+    }
+
+    .footer {
+    	width: auto;
+    }
+
+    .bodywrapper {
+    	margin: 0;
+    }
+
+    .footer {
+    	width: auto;
+    }
+
+    .github {
+        display: none;
+    }
+
+
+
+}
+
+
+
+@media screen and (max-width: 875px) {
+
+    body {
+        margin: 0;
+        padding: 20px 30px;
+    }
+
+    div.documentwrapper {
+        float: none;
+        background: white;
+    }
+
+    div.sphinxsidebar {
+        display: block;
+        float: none;
+        width: 102.5%;
+        margin: 50px -30px -20px -30px;
+        padding: 10px 20px;
+        background: #333;
+        color: white;
+    }
+
+    div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
+    div.sphinxsidebar h3 a {
+        color: white;
+    }
+
+    div.sphinxsidebar a {
+        color: #aaa;
+    }
+
+    div.sphinxsidebar p.logo {
+        display: none;
+    }
+
+    div.document {
+        width: 100%;
+        margin: 0;
+    }
+
+    div.related {
+        display: block;
+        margin: 0;
+        padding: 10px 0 20px 0;
+    }
+
+    div.related ul,
+    div.related ul li {
+        margin: 0;
+        padding: 0;
+    }
+
+    div.footer {
+        display: none;
+    }
+
+    div.bodywrapper {
+        margin: 0;
+    }
+
+    div.body {
+        min-height: 0;
+        padding: 0;
+    }
+
+    .rtd_doc_footer {
+        display: none;
+    }
+
+    .document {
+        width: auto;
+    }
+
+    .footer {
+        width: auto;
+    }
+
+    .footer {
+        width: auto;
+    }
+
+    .github {
+        display: none;
+    }
+}
+
+
+/* misc. */
+
+.revsys-inline {
+    display: none!important;
+}
+
+button {
+  padding: 4px 12px;
+  font-size:14px;
+  background:#dadada;
+  vertical-align:middle;
+  cursor:pointer;
+  color:#333;
+  text-align:center;
+  line-height:20px;
+  border-radius:4px;
+  box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0,  0.05);
+  border:1px solid #ccc;
+  -webkit-appearance: button;
+  box-sizing: border-box;
+  font-weight:700;
+  font-family:Helvetica, sans-serif;
+ }
+
+ button:hover { background:#0e84b5 }
+ button:hover a { color:#fff!important }
+
+ button a {
+   text-decoration:none!important;
+   color:#333!important;
+   font:bold 11px/14px "Helvetica Neue",Helvetica,Arial,sans-serif;
+  }
+
+  button.tpl a:after { content:' »'; }
+

+ 19 - 0
doc/source/_themes/aj/theme.conf

@@ -0,0 +1,19 @@
+[theme]
+inherit = basic
+stylesheet = flasky.css
+pygments_style = flask_theme_support.FlaskyStyle
+
+[options]
+package_url = http://pypi.python.org/pypi/mico
+repo_url = https://github.com/ajdiaz/mico
+repo_name = mico
+repo_email = ajdiaz@connectical.com
+repo_author = ajdiaz
+logo_path = _static/mico.jpg
+logo_title = The mico monkey
+author_twitter = andresjdiaz
+description = Mico is a powerful system to deploy
+	new machines, volumes, autoscaling groups and
+	so in in the cloud programatically and easily,
+	just as a monkey does.
+

+ 86 - 0
doc/source/_themes/flask_theme_support.py

@@ -0,0 +1,86 @@
+# flasky extensions.  flasky pygments style based on tango style
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+     Number, Operator, Generic, Whitespace, Punctuation, Other, Literal
+
+
+class FlaskyStyle(Style):
+    background_color = "#f8f8f8"
+    default_style = ""
+
+    styles = {
+        # No corresponding class for the following:
+        #Text:                     "", # class:  ''
+        Whitespace:                "underline #f8f8f8",      # class: 'w'
+        Error:                     "#a40000 border:#ef2929", # class: 'err'
+        Other:                     "#000000",                # class 'x'
+
+        Comment:                   "italic #8f5902", # class: 'c'
+        Comment.Preproc:           "noitalic",       # class: 'cp'
+
+        Keyword:                   "bold #004461",   # class: 'k'
+        Keyword.Constant:          "bold #004461",   # class: 'kc'
+        Keyword.Declaration:       "bold #004461",   # class: 'kd'
+        Keyword.Namespace:         "bold #004461",   # class: 'kn'
+        Keyword.Pseudo:            "bold #004461",   # class: 'kp'
+        Keyword.Reserved:          "bold #004461",   # class: 'kr'
+        Keyword.Type:              "bold #004461",   # class: 'kt'
+
+        Operator:                  "#582800",   # class: 'o'
+        Operator.Word:             "bold #004461",   # class: 'ow' - like keywords
+
+        Punctuation:               "bold #000000",   # class: 'p'
+
+        # because special names such as Name.Class, Name.Function, etc.
+        # are not recognized as such later in the parsing, we choose them
+        # to look the same as ordinary variables.
+        Name:                      "#000000",        # class: 'n'
+        Name.Attribute:            "#c4a000",        # class: 'na' - to be revised
+        Name.Builtin:              "#004461",        # class: 'nb'
+        Name.Builtin.Pseudo:       "#3465a4",        # class: 'bp'
+        Name.Class:                "#000000",        # class: 'nc' - to be revised
+        Name.Constant:             "#000000",        # class: 'no' - to be revised
+        Name.Decorator:            "#888",           # class: 'nd' - to be revised
+        Name.Entity:               "#ce5c00",        # class: 'ni'
+        Name.Exception:            "bold #cc0000",   # class: 'ne'
+        Name.Function:             "#000000",        # class: 'nf'
+        Name.Property:             "#000000",        # class: 'py'
+        Name.Label:                "#f57900",        # class: 'nl'
+        Name.Namespace:            "#000000",        # class: 'nn' - to be revised
+        Name.Other:                "#000000",        # class: 'nx'
+        Name.Tag:                  "bold #004461",   # class: 'nt' - like a keyword
+        Name.Variable:             "#000000",        # class: 'nv' - to be revised
+        Name.Variable.Class:       "#000000",        # class: 'vc' - to be revised
+        Name.Variable.Global:      "#000000",        # class: 'vg' - to be revised
+        Name.Variable.Instance:    "#000000",        # class: 'vi' - to be revised
+
+        Number:                    "#990000",        # class: 'm'
+
+        Literal:                   "#000000",        # class: 'l'
+        Literal.Date:              "#000000",        # class: 'ld'
+
+        String:                    "#4e9a06",        # class: 's'
+        String.Backtick:           "#4e9a06",        # class: 'sb'
+        String.Char:               "#4e9a06",        # class: 'sc'
+        String.Doc:                "italic #8f5902", # class: 'sd' - like a comment
+        String.Double:             "#4e9a06",        # class: 's2'
+        String.Escape:             "#4e9a06",        # class: 'se'
+        String.Heredoc:            "#4e9a06",        # class: 'sh'
+        String.Interpol:           "#4e9a06",        # class: 'si'
+        String.Other:              "#4e9a06",        # class: 'sx'
+        String.Regex:              "#4e9a06",        # class: 'sr'
+        String.Single:             "#4e9a06",        # class: 's1'
+        String.Symbol:             "#4e9a06",        # class: 'ss'
+
+        Generic:                   "#000000",        # class: 'g'
+        Generic.Deleted:           "#a40000",        # class: 'gd'
+        Generic.Emph:              "italic #000000", # class: 'ge'
+        Generic.Error:             "#ef2929",        # class: 'gr'
+        Generic.Heading:           "bold #000080",   # class: 'gh'
+        Generic.Inserted:          "#00A000",        # class: 'gi'
+        Generic.Output:            "#888",           # class: 'go'
+        Generic.Prompt:            "#745334",        # class: 'gp'
+        Generic.Strong:            "bold #000000",   # class: 'gs'
+        Generic.Subheading:        "bold #800080",   # class: 'gu'
+        Generic.Traceback:         "bold #a40000",   # class: 'gt'
+    }

+ 9 - 0
doc/source/api.rst

@@ -0,0 +1,9 @@
+API Reference
+-------------
+
+.. toctree::
+   :maxdepth: 4
+
+   mico.lib
+
+

+ 246 - 0
doc/source/conf.py

@@ -0,0 +1,246 @@
+# -*- coding: utf-8 -*-
+#
+# mico documentation build configuration file, created by
+# sphinx-quickstart on Tue Apr  9 18:34:43 2013.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+sys.path.insert(0, os.path.abspath('../..'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'mico'
+copyright = u'2013, Andrés J. Díaz'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '1.0'
+# The full version, including alpha/beta/rc tags.
+release = '1.0'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = []
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'aj'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+html_theme_path = ['_themes']
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+html_sidebars = {
+    '**': ['sidebar.html', 'searchbox.html', 'relations.html'],
+}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'micodoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+# The paper size ('letterpaper' or 'a4paper').
+#'papersize': 'letterpaper',
+
+# The font size ('10pt', '11pt' or '12pt').
+#'pointsize': '10pt',
+
+# Additional stuff for the LaTeX preamble.
+#'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'mico.tex', u'mico Documentation',
+   u'Andrés J. Díaz', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'mico', u'mico Documentation',
+     [u'Andrés J. Díaz'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+  ('index', 'mico', u'mico Documentation',
+   u'Andrés J. Díaz', 'mico', 'One line description of project.',
+   'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+

+ 63 - 0
doc/source/contributing.rst

@@ -0,0 +1,63 @@
+============
+Contributing
+============
+
+For more information, please see the official contribution docs at
+http://mico.ajdiaz.me/en/latest/contributing.html.
+
+
+Contributing Code
+=================
+
+* A good patch:
+
+  * is clear.
+  * works across all supported versions of Python.
+  * follows the existing style of the code base (PEP-8).
+  * has comments included as needed.
+
+* A test case that demonstrates the previous flaw that now passes
+  with the included patch.
+* If it adds/changes a public API, it must also include documentation
+  for those changes.
+* Must be appropriately licensed (GPLv2 or newer version).
+
+
+Reporting An Issue/Feature
+==========================
+
+* Check to see if there's an existing issue/pull request for the
+  bug/feature. All issues are at https://github.com/ajdiaz/mico/issues
+  and pull reqs are at https://github.com/ajdiaz/mico/pulls.
+* If there isn't an existing issue there, please file an issue. The ideal
+  report includes:
+
+  * A description of the problem/suggestion.
+  * How to recreate the bug.
+  * If relevant, including the versions of your:
+
+    * Python interpreter
+    * mico
+    * Optionally of the other dependencies involved
+
+  * If possile, create a pull request with a (failing) test case demonstrating
+    what's wrong. This makes the process for fixing bugs quicker & gets issues
+    resolved sooner.
+
+What we need?
+=============
+
+* To improve mico we need a lot of *new libraries*, core libraries is our main
+  focus right now, for next versions of mic we like to have a lot of core
+  libs, such like change parameters in the OS, modify some base system
+  config, or more high level ones like "Install a full LAMP architecture".
+  Every new library is welcome.
+
+* We need more pre-defined templates, like "ls" does. For example a cost
+  template to calculate EC2 budget.
+
+* More EC2 libraries, for example for S3.
+
+* Fix bugs... we need to remove the Beta tag ;)
+
+

+ 8 - 0
doc/source/decorators.rst

@@ -0,0 +1,8 @@
+Global Decorators
+=================
+
+
+.. automodule:: mico.decorators
+  :members:
+
+

+ 24 - 0
doc/source/environ.rst

@@ -0,0 +1,24 @@
+The Environment
+---------------
+
+The environment is a global dictionary which is always available in mico
+with the keyworkd ``env``. The environment content some information about
+the host where mico is working currently and also contain other some useful
+things.
+
+The custon environment, available via ``env.custom``, is a dictionary which
+contains some variables which are getted from remote host. For example:
+
+.. code-block:: python
+
+  print env.custom.ip_address
+
+Will print the IP address of the remote host.
+
+There is full list of available custom environments:
+
+.. automodule:: mico.environ
+    :members:
+    :undoc-members:
+    :show-inheritance:
+

+ 100 - 0
doc/source/index.rst

@@ -0,0 +1,100 @@
+Mico: a monkey in the cloud
+===========================
+
+Mico is a tool-toy to manage a number of hosts deployed in cloud services
+(currently only support Amazon AWS), and also allows you to deploy new hosts
+with specified template or create autoscaling groups and manage them easily.
+
+.. raw:: html
+
+  <a href="https://twitter.com/share" class="twitter-share-button"
+  data-via="andresjdiaz" data-hashtags="mico">Tweet</a>
+  <script>!function(d,s,id){var
+  js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
+
+  <iframe
+  src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fmico.readthedocs.org&amp;send=false&amp;layout=button_count&amp;width=450&amp;show_faces=true&amp;font=verdana&amp;colorscheme=light&amp;action=like&amp;height=21"
+  scrolling="no" frameborder="0" style="border:none; overflow:hidden;
+  width:450px; height:21px;" allowTransparency="true"></iframe>
+
+
+Installation
+------------
+As usual, mico is available from pypi_, and can be installed using ``pip``::
+
+  pip install mico
+
+.. _pypi: http://pypi.python.org/pypi
+
+Mico just need an AWS key ID and AWS secret key to run. By default mico just
+take this variables from the OS environment::
+
+    export AWS_ACCESS_KEY_ID="*foo*"
+    export AWS_SECRET_ACCESS_KEY="*bar*"
+
+
+QuickStart
+----------
+Mico works using the concept of template. A template is just a python code
+(with steroids which we call *libraries*), the template can implements
+a number of actions to perform in the cloud. In this example we just create
+a new host in AWS and install some packages there.
+
+.. code-block:: python
+
+    from mico.lib.aws import *
+    from mico.lib.core import *
+
+    def deploy(*args):
+        for host in args:
+            instance = ec2_ensure(
+                ami = "ami-3d4ff254",
+                name = host,
+                instance_type = "t1.micro",
+                key_name = "root-us-east-virginia",
+                security_groups = "sec-test"
+            )
+
+            package_ensure("python") # of course :)
+            package_ensure("apache")
+
+
+Once, your template is created, you need to put it into a mico template path
+(by default uses ``/etc/mico`` and ``~/.config/mico/``, and the current
+working directory.
+
+Then you can just run mico
+
+.. code-block:: bash
+
+    $ mico template:deploy myhost1.mydomain.com myhost2.mydomain.com
+    mico:cloud:deploy:create security group: sec-test
+    mico:cloud:deploy:create instance: i-4543123
+    mico:cloud:deploy:use existent security group: sec-test
+    mico:cloud:deploy:create instance: i-2291281
+
+
+You can see more complex (and useful!) templates in `examples directory`_.
+
+.. _`examples directory`: tree/master/examples
+
+.. raw:: html
+
+  <button class="tpl"><a href="https://github.com/ajdiaz/mico/tree/master/examples">View template
+  examples</a></button>
+
+
+
+User Guide
+----------
+
+.. toctree::
+  :maxdepth: 4
+
+  templates
+  decorators
+  environ
+  api
+  contributing
+
+

+ 54 - 0
doc/source/mico.lib.aws.ec2.rst

@@ -0,0 +1,54 @@
+
+.. automodule:: mico.lib.aws.ec2
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`autoscale` Module
+-----------------------
+
+.. automodule:: mico.lib.aws.ec2.autoscale
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`cw` Module
+----------------
+
+.. automodule:: mico.lib.aws.ec2.cw
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`ebs` Module
+-----------------
+
+.. automodule:: mico.lib.aws.ec2.ebs
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`eip` Module
+-----------------
+
+.. automodule:: mico.lib.aws.ec2.eip
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`elb` Module
+-----------------
+
+.. automodule:: mico.lib.aws.ec2.elb
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`sg` Module
+----------------
+
+.. automodule:: mico.lib.aws.ec2.sg
+    :members:
+    :undoc-members:
+    :show-inheritance:
+

+ 18 - 0
doc/source/mico.lib.aws.rst

@@ -0,0 +1,18 @@
+The AWS library: mico.lib.aws
+=============================
+
+:mod:`r53` Module
+-----------------
+
+.. automodule:: mico.lib.aws.r53
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`ec2` Module
+-----------------
+
+.. toctree::
+
+    mico.lib.aws.ec2
+

+ 21 - 0
doc/source/mico.lib.core.package.rst

@@ -0,0 +1,21 @@
+.. automodule:: mico.lib.core.package
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`apt` Module
+-----------------
+
+.. automodule:: mico.lib.core.package.apt
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`yum` Module
+-----------------
+
+.. automodule:: mico.lib.core.package.yum
+    :members:
+    :undoc-members:
+    :show-inheritance:
+

+ 90 - 0
doc/source/mico.lib.core.rst

@@ -0,0 +1,90 @@
+The core library: mico.lib.core
+===============================
+
+:mod:`dir` Module
+-----------------
+
+.. automodule:: mico.lib.core.dir
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`file` Module
+------------------
+
+.. automodule:: mico.lib.core.file
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`group` Module
+-------------------
+
+.. automodule:: mico.lib.core.group
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`local` Module
+-------------------
+
+.. automodule:: mico.lib.core.local
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`network` Module
+---------------------
+
+.. automodule:: mico.lib.core.network
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`service` Module
+---------------------
+
+.. automodule:: mico.lib.core.service
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`ssh` Module
+-----------------
+
+.. automodule:: mico.lib.core.ssh
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`sudo` Module
+------------------
+
+.. automodule:: mico.lib.core.sudo
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`user` Module
+------------------
+
+.. automodule:: mico.lib.core.user
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`virtualenv` Module
+------------------------
+
+.. automodule:: mico.lib.core.virtualenv
+    :members:
+    :undoc-members:
+    :show-inheritance:
+
+:mod:`package` Package
+----------------------
+
+.. toctree::
+
+    mico.lib.core.package
+

+ 5 - 0
doc/source/mico.lib.rst

@@ -0,0 +1,5 @@
+.. toctree::
+
+    mico.lib.aws
+    mico.lib.core
+

+ 63 - 0
doc/source/templates.rst

@@ -0,0 +1,63 @@
+Writing templates
+-----------------
+
+Templates are the base of mico. Mico by default looking for templates in
+current working directory as well as ``/etc/mico`` and ``~/.config/mico``.
+
+Template is a python file which can access to all mico builtins methods
+(like ``@serial`` or ``@async`` decorators), and also functions in libraries
+where imported.
+
+To call a template just run mico with the template name as first argument.
+Optionally you can specify a function to run, in the form::
+
+  $ mico template:function
+
+If not function specified, then use ``main`` function by default.
+
+Here are a basic template for mico:
+
+.. code-block:: python
+
+    from mico.lib.aws import *
+    from mico.lib.core import *
+
+    def deploy(\*args):
+        for host in args:
+            host, domain = host.split('.', 1) \
+                           if '.' in host \
+                           else (host, 'localdomain')
+
+            sg_test = sg_ensure(
+                name        = "sg-test",
+                description = "security group to test mico",
+                rules = [
+                    sg_rule(
+                        protocol = "tcp",
+                        source   = "0.0.0.0/0",
+                        port     = "22"
+                    ),
+                    sg_rule(
+                        protocol = "tcp",
+                        source = "0.0.0.0/0",
+                        port = "80"
+                    )
+                ]
+            )
+
+            instance = ec2_ensure(
+                ami = "ami-3d4ff254",
+                name = host,
+                instance_type = "t1.micro",
+                key_name = "root-us-east-virginia",
+                security_groups = sg_test
+           )
+
+
+.. note:: You don't need to be worried about how to link EC2 instance with
+    host where local commands will be executed. In previous example the
+    *pkg_ensure* command will be executed rightly in the instance which has
+    been created in the previous line with the *ec2_ensure* command.
+
+    This works fine, because mico works sequentially for each task.
+

+ 144 - 0
examples/complex.py

@@ -0,0 +1,144 @@
+from mico.lib.aws.ec2 import *
+from mico.lib.core import *
+
+USERDATA = """
+#cloud-config
+hostname: %s
+fqdn: %s
+manage_etc_hosts: true
+locale: C
+timezone: Europe/Madrid
+apt_update: true
+apt_upgrade: true
+"""
+
+def main(*args):
+    "Deploy the entire nosy production environment"
+
+    import pickle
+    pickle.dump(create_security,open("/tmp/caca","w"))
+
+    for arg in args:
+        create_security(arg)
+        create_instance(arg)
+
+        install_nginx(arg)
+        install_uwsgi(arg)
+        install_python(arg)
+
+        create_users(arg)
+        configure_sudoers(arg)
+
+        install_global_pip_packages(arg)
+        create_virtualenv(arg)
+        start_services(arg)
+
+
+
+
+def create_security(args):
+    "Create security groups required for the environment."
+
+    sg_frontend = sg_ensure(
+            name        = "frontend",
+            description = 'frontend security group',
+            rules = [
+                sg_rule(
+                    protocol = "tcp",
+                    source   = "0.0.0.0/0",
+                    port     = "22"
+                ),
+                sg_rule(
+                    protocol = "tcp",
+                    source = "0.0.0.0/0",
+                    port = "80"
+                )
+            ]
+    )
+
+
+def create_instance(arg):
+    "Create EBS instance for nosy."
+
+    instance = ec2_ensure(
+            ami = "ami-d0f89fb9",
+            name = arg,
+            instance_type = "t1.micro",
+            user_data = USERDATA % ( arg, "%s.example.com" % arg,) ,
+            key_name = "example",
+            security_groups = [ "frontend" ]
+    )
+
+
+def install_nginx(arg):
+    "Install nginx host."
+
+    with mode_sudo():
+        package_ensure("nginx-full")
+        service_ensure_boot("nginx")
+
+
+
+def install_uwsgi(args):
+    "Install the uwsgi components into the system."
+
+    with mode_sudo():
+        package_ensure("uwsgi")
+        package_ensure("uwsgi-infrastructure-plugins")
+        package_ensure("uwsgi-plugin-python")
+
+
+def install_python(args):
+    "Install python environment."
+
+    with mode_sudo():
+        package_ensure("python")
+        package_ensure("python-virtualenv")
+        package_ensure("python-pip")
+
+
+def create_users(args):
+    "Create users for host."
+
+    with mode_sudo():
+        user_ensure("ajdiaz")
+        group_user_add("sudo","ajdiaz")
+        ssh_authorize("ajdiaz","ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDX0Ila4UIQkgYRCTeHGzXkZbqYEpwwDlUDDM3oM4j2/hkrd9AY5p/ug921TpN4qi5pooSRwcD5ZiWZNqzPfEefl4A+3pGVfObN3NNUbc0Iry/T6MRdmnWB3LQxc2R45UOhVjLTuCKWRNLyZ1A7sIp7yrBt36BHoSoL40AIBbdEp37oSgubCI953UGNIN70BGAF/Cm0SW5f47NqDM2N2Fz/LA6Zf3NYXqYRzkOkpHxJ10DUvaAXoLiFVQPEGVdgwQJpIUdGZYkhPbgs5yBhBU5y2BLABJb/b1Yt3Yl6qSuBNOPP4oIMYjkUWXHu81hYJe68GpBwWN1AwGv3g7LeFOcx near.to")
+
+
+def configure_sudoers(args):
+    "Configure sudoers file."
+    with mode_sudo():
+        file_content(
+            src="data/sudoers.tpl",
+            dst="/etc/sudoers2",
+        )
+
+
+def install_global_pip_packages(args):
+    "Install pip packages for host."
+    PIP_PACKAGES = [ "redis", "bottle" ]
+
+    for package in PIP_PACKAGES:
+        sudo("pip install --upgrade %s" % package)
+
+
+def create_virtualenv(args):
+    "Create virtualenv for host."
+    with mode_sudo():
+        dir_ensure("/srv/app/src")
+        dir_ensure("/srv/app/env")
+    sudo("virtualenv /srv/app/env")
+
+
+def start_services(args):
+    "Start boot services if not running."
+    service_ensure("nginx")
+    service_reload("nginx")
+
+    service_ensure("uwsgi")
+    service_restart("uwsgi")
+
+    service_ensure_boot("uwsgi")
+
+

+ 30 - 0
examples/data/sudoers.tpl

@@ -0,0 +1,30 @@
+#
+# This file MUST be edited with the 'visudo' command as root.
+#
+# Please consider adding local content in /etc/sudoers.d/ instead of
+# directly modifying this file.
+#
+# See the man page for details on how to write a sudoers file.
+#
+Defaults        env_reset
+Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+
+# Host alias specification
+
+# User alias specification
+
+# Cmnd alias specification
+
+# User privilege specification
+root    ALL=(ALL:ALL) ALL
+
+# Members of the admin group may gain root privileges
+%admin ALL=(ALL) ALL
+
+# Allow members of group sudo to execute any command
+%sudo   ALL=(ALL:ALL) NOPASSWD: ALL
+
+# See sudoers(5) for more information on "#include" directives:
+#includedir /etc/sudoers.d
+
+

+ 53 - 0
examples/ec2.py

@@ -0,0 +1,53 @@
+from mico.lib.aws import *
+
+USERDATA = """#cloud-config
+hostname: %s
+fqdn: %s
+manage_etc_hosts: true
+locale: C
+timezone: Europe/Madrid
+apt_update: true
+apt_upgrade: true
+"""
+
+def main(*args):
+    for arg in args:
+        deploy(arg)
+
+@async
+def deploy(host):
+    """Generic deploy system for a number of hosts.
+
+    Example::
+        deploy('host1', 'host2')
+    """
+
+    host, domain = host.split('.', 1) if '.' in host else (host, 'localdomain')
+
+    sg_test = sg_ensure(
+        name        = "sec-test",
+        description = "security group to test mico",
+        rules = [
+            sg_rule(
+                protocol = "tcp",
+                source   = "0.0.0.0/0",
+                port     = "22"
+            ),
+            sg_rule(
+                protocol = "tcp",
+                source = "0.0.0.0/0",
+                port = "80"
+            )
+        ]
+    )
+
+    instance = ec2_ensure(
+        ami = "ami-3d4ff254",
+        name = host,
+        instance_type = "t1.micro",
+        user_data = USERDATA % ( host, "%s.%s" % (host, domain,) ),
+        key_name = "root-us-east-virginia",
+        security_groups = [ sg_test ]
+    )
+
+

+ 22 - 0
examples/simple.py

@@ -0,0 +1,22 @@
+from mico.lib.aws.ec2 import *
+from mico.lib.core import *
+
+USERDATA = """
+#cloud-config
+hostname: %s
+fqdn: %s
+manage_etc_hosts: true
+locale: C
+timezone: Europe/Madrid
+apt_update: true
+apt_upgrade: true
+"""
+
+def main(*args):
+    "Deploy the entire nosy production environment"
+
+    import pickle
+    x = pickle.load(open("/tmp/caca","r"))
+
+    x("a")
+

+ 31 - 0
examples/stupid.py

@@ -0,0 +1,31 @@
+"""This is a very basic template example. This template do nothing, just
+print a message in stdout. Of course, it not very useful :)
+"""
+
+import random
+from time import sleep
+
+def hello():
+    print "Hello world!"
+
+def bye():
+    print "Good bye cruel world..."
+
+@async
+def launch_async(arg):
+    sleep(random.randint(2,10))
+    print "Hola %s" % arg
+
+@serial
+def pfinish():
+    print "Finished!"
+
+def main(*args):
+    # next two in serial
+    hello()
+    bye()
+    # next in parallel
+    for x in args:
+        launch_async(x)
+    pfinish()
+

+ 118 - 0
mico/__init__.py

@@ -0,0 +1,118 @@
+#! /usr/bin/env python
+# -*- encoding: utf-8 -*-
+# vim:fenc=utf-8:
+
+import os
+import sys
+
+# Set the configuration PATH
+config_path = [
+        os.curdir,
+        os.path.join(os.environ.get("HOME","/"), ".config/mico"),
+        "/etc/mico",
+        os.path.join(os.path.dirname(__file__), "templates"),
+        os.path.join(os.curdir, "files")
+]
+
+if os.environ.get("MICO_CONFIG_PATH", None):
+    config_path.insert(0, os.environ.get("MICO_CONFIG_PATH"))
+
+# Set the cache PATH
+cache_path = os.environ.get("MICO_CACHE_PATH", None) or \
+             os.path.join(os.environ.get("HOME","/"), ".cache/mico")
+
+# Set the lib PATH
+lib_path = [
+        os.path.join(os.environ.get("HOME","/"), ".local/share/mico"),
+        "/usr/lib/mico",
+        "/usr/local/lib/mico",
+        "/usr/share/mico"
+        "/usr/local/share/mico"
+]
+
+if os.environ.get("MICO_LIBRARY_PATH", None):
+    lib_path.insert(0, os.environ.get("MICO_LIBRARY_PATH"))
+
+sys.path.extend(lib_path)
+sys.path.extend(config_path)
+
+import __builtin__
+from mico.util.dicts import AutoCreatedLazyDict
+from fabric.api import env
+
+__builtin__.env = env
+__builtin__.env.custom = AutoCreatedLazyDict(env)
+
+import mico.environ
+
+import mico.hook
+from fabric.tasks import execute as fabric_execute
+
+def execute(action, *args, **kwargs):
+    """Exec allows you to execute an action using fabric API, adding pre-hook
+    and post-hook into the pipeline if required."""
+
+    return fabric_execute(
+             mico.hook.task_add_pre_run_hook( mico.hook.run_pre_hook )
+                ( mico.hook.task_add_post_run_hook( mico.hook.run_post_hook )
+                    ( action )), *args, **kwargs )
+
+import fabric.api
+from mico.lib.core.sudo import is_sudo
+from mico.lib.core.local import is_local, run_local
+
+def run(*args, **kwargs):
+    """A wrapper to Fabric's run/sudo commands that takes into account
+    the mode_local.
+    """
+    if "force" in kwargs:
+        force = kwargs["force"]
+        del kwargs["force"]
+    else:
+        force = False
+
+    if is_local():
+        if is_sudo():
+            kwargs.setdefault("sudo", True)
+        _exe = run_local(*args, **kwargs)
+    else:
+        mico.output.debug(" ".join(args))
+        if is_sudo():
+            _exe = fabric.api.sudo(*args, **kwargs)
+        else:
+            _exe = fabric.api.run(*args, **kwargs)
+
+    if _exe.return_code != 0 and not force:
+        raise ExecutionError("%s: failed to run '%s' (retcode:%d): '%s'" % (
+            env.host_string,
+            " ".join(args),
+            _exe.return_code,
+            str(_exe)
+        ))
+    else:
+        return _exe
+
+class ExecutionError(Exception):
+    """Models an execution error"""
+
+__builtin__.run_local = run_local
+__builtin__.run = run
+
+
+from mico.decorators import async, serial, parallel, sync
+__builtin__.async = async
+__builtin__.serial = serial
+__builtin__.parallel = parallel
+__builtin__.sync = sync
+
+from mico.util.storage import FileStorage
+__builtin__.env.storage = FileStorage(cache_path)
+
+def save(key, data):
+    "Save a data in the global cache catalog."
+    env.storage[key] = data
+
+def load(key):
+    "Get a data saved in global cache catalog."
+    return env.storage[key]
+

+ 93 - 0
mico/decorators.py

@@ -0,0 +1,93 @@
+#! /usr/bin/env python
+# -*- encoding: utf-8 -*-
+# vim:fenc=utf-8:
+
+from threading import Thread
+from functools import wraps
+
+from fabric.api import parallel as fabric_parallel
+from mico.util.mutex import Mutex
+
+parallel = fabric_parallel
+
+
+def async(func):
+    """Function decorator, intended to make "func" run in a separate
+    thread  (asynchronously).
+
+    Example:
+
+    .. code-block:: python
+
+        @async
+        def task1():
+            do_something
+
+        @async
+        def task2():
+            do_something_too
+
+        t1 = task1()
+        t2 = task2()
+
+        t1.join()
+        t2.join()
+	"""
+
+    @wraps(func)
+    def _inner(*args, **kwargs):
+        func_th = Thread(target = func, args = args, kwargs = kwargs)
+        func_th.start()
+        return func_th
+
+    return _inner
+
+def serial(func):
+    """Function decorator, intended to make "func" wait for threading async
+    finished.
+
+    Example:
+
+    .. code-block:: python
+
+        @async
+        def task1():
+            do_something
+
+        @async
+        def task2():
+            do_something_too
+
+        @serial
+        def task3():
+            do_something_when_task1_and_task2_finished()
+
+        t1 = task1()
+        t2 = task2()
+        t3 = task3() # Only runs when task1 and task2 finished.
+    """
+
+    @wraps(func)
+    def _inner(*args, **kwargs):
+        import threading, time
+        while threading.activeCount() > 1:
+            time.sleep(1)
+        return func(*args, **kwargs)
+    return _inner
+
+
+def sync(f):
+    """Decorator to synchronize a function while running in asynchronous
+    mode. Let's suppose that you have a number of tasks decorated by
+    ``@async``, but you need to ensure that one common action is
+    synchronized over all of them, then ``@sync`` is for you.
+
+    .. note:: The ``@sync`` decorator is radically different of ``@serial``.
+        While ``@serial`` waits for every thread to be done, ``@sync`` just only
+        ensure that the decorated function is calling once a time.
+    """
+    def _f(*args, **kw):
+        with Mutex.get_mutex(f.__name__):
+            return f(*args, **kw)
+    return _f
+

+ 126 - 0
mico/environ.py

@@ -0,0 +1,126 @@
+#! /usr/bin/env python
+# -*- encoding: utf-8 -*-
+# vim:fenc=utf-8:
+
+"""The environ module provides a number of environ variables, which will be
+used in your definitions and templates to ensure the correct way to do
+actions. Usual environments are related with remote host, like installed
+operating system, versions and so on.
+
+Also, this module provides a decorator to create new environment
+properties."""
+
+import sys
+
+import mico
+from mico.util.dicts import AttrLazyDict
+
+def environ(name):
+    """Decorator to set new environment variables. Using this decoration you
+    can create environment dynamic properties, for example, you can set new
+    one environ called 'uptime' which contains the remote system uptime
+    using the core template function 'uptime'::
+
+        @environ('uptime')
+        def _env_uptime():
+            return mico.lib.core.uptime()
+
+    Then you can use the new custom environ in the form::
+
+        print env.custom.uptime
+
+    Or in the old and good dictionary style::
+
+        print env.custom["uptime"]
+
+    """
+
+    def _decorator(fn):
+        setattr(sys.modules[__name__], name, fn)
+        mico.env.custom[name] = fn
+    return _decorator
+
+
+@environ('kernel')
+def _env_kernel():
+    """Get the kernel version running in the remote host.
+    """
+    return mico.run("uname -s").lower()
+
+from mico.lib.core import file_exists
+from mico.lib.core import file_read
+
+@environ('operating_system')
+def _env_operation_system():
+    """Get the Operating System of the remote host.
+    """
+    # TODO: Add other operating systems here...
+    if env.custom.kernel == "linux":
+        if file_exists("/etc/lsb-release"):
+            return "ubuntu"
+        if file_exists("/etc/debian_version"):
+            return "debian"
+        if file_exists("/etc/gentoo-release"):
+            return "gentoo"
+        if file_exists("/etc/fedora-release"):
+            return "fedora"
+        if file_exists("/etc/mandriva-release"):
+            return "mandriva"
+        if file_exists("/etc/mandrake-release"):
+            return "mandrake"
+        if file_exists("/etc/meego-release"):
+            return "meego"
+        if file_exists("/etc/arch-release"):
+            return "arch"
+        if file_exists("/etc/oracle-release"):
+            return "oracle"
+        if file_exists("/etc/vmware-release"):
+            return "vmware"
+        if file_exists("/etc/redhat-release"):
+            dist = file_read("/etc/redhat-release").lower()
+            if "centos" in dist:
+                return "centos"
+            # TODO: Add other exotic redhat based distributions
+            else:
+                return "redhat"
+        if file_exists("/etc/SuSe-release"):
+            dist = file_read("/etc/SuSe-release").lower()
+            if "suse linux enterprise server" in dist:
+                return "sles"
+            if "suse linux enterprise desktop" in dist:
+                return "sled"
+            if "opensuse" in dist:
+                return "opensuse"
+            else:
+                return "suse"
+        if file_exists("/etc/slackware-release"):
+            return "slackware"
+        if file_exists("/etc/system-release"):
+            return "amazon"
+    else:
+        return "unknown"
+
+from mico.lib.core import network_address
+from mico.lib.core import network_interfaces
+@environ('ipaddr')
+def _env_ipaddr():
+    """Returns a dictionary with the IP address of the remote hosts,
+    indexed by interface. For example:
+
+    .. code-block:: python
+
+        ips = env.cutom.ip_addr
+        print ips['eth0']
+    """
+    ret = AttrLazyDict()
+    for iface in network_interfaces():
+        ret[iface] = network_address(iface)
+    return ret
+
+from mico.lib.core import network_nameservers
+@environ('nameservers')
+def _env_nameservers():
+    """Returns the nameserver configured in the remote host.
+    """
+    return network_nameservers()
+

+ 37 - 0
mico/fifo.py

@@ -0,0 +1,37 @@
+#! /usr/bin/env python
+# -*- encoding: utf-8 -*-
+# vim:fenc=utf-8:
+
+"""Queues are using to enqueue some actions which will be performed after
+a task execution."""
+
+from Queue import Queue
+
+def put(action, parameters=()):
+    """Enqueue one action with action parameters in environment"""
+
+    if "mico_queue" in env:
+        env["mico_queue"].put((action,parameters))
+    else:
+        env["mico_queue"] = Queue()
+        env["mico_queue"].put((action,parameters))
+
+
+def run():
+    """Run enqueued actions"""
+    if "mico_queue" in env:
+        ret = []
+        q = env["mico_queue"]
+        while not q.empty():
+            action, parameters = q.get()
+            ret.append(action(*parameters))
+        return ret
+    return []
+
+
+def get():
+    """Get an element of the queue"""
+    if "mico_queue" in env:
+        q = env["mico_queue"]
+        return q.get()
+

+ 143 - 0
mico/hook.py

@@ -0,0 +1,143 @@
+#! /usr/bin/env python
+# -*- encoding: utf-8 -*-
+# vim:fenc=utf-8:
+
+"""Hooks are useful toy for fabric. Current versions of fabric do not
+support post action hooks."""
+
+import traceback
+from functools import wraps
+
+import mico
+import mico.output
+
+from __builtin__ import env
+
+# global variable for add_hooks()
+parent_task_name = ''
+
+def task_add_post_run_hook(hook, *args, **kwargs):
+    '''Run hook after Fabric tasks have completed on all hosts
+
+    Example usage:
+        @add_post_run_hook(postrunfunc, 'arg1', 'arg2')
+        def mytask():
+            # ...
+
+    '''
+    def true_decorator(f):
+        return add_hooks(post=hook, post_args=args, post_kwargs=kwargs)(f)
+    return true_decorator
+
+
+def task_add_pre_run_hook(hook, *args, **kwargs):
+    '''Run hook before Fabric tasks have completed on all hosts
+
+    Example usage:
+        @add_pre_run_hook(prerunfunc, 'arg1', 'arg2')
+        def mytask():
+            # ...
+
+    '''
+    def true_decorator(f):
+        return add_hooks(pre=hook, pre_args=args, pre_kwargs=kwargs)(f)
+    return true_decorator
+
+
+def add_hooks(pre=None, pre_args=(), pre_kwargs={},
+              post=None, post_args=(), post_kwargs={}):
+    '''
+    Function decorator to be used with Fabric tasks.  Adds pre-run
+    and/or post-run hooks to a Fabric task.  Uses env.all_hosts to
+    determine when to run the post hook.  Uses the global variable,
+    parent_task_name, to check if the task is a subtask (i.e. a
+    decorated task called by another decorated task). If it is a
+    subtask, do not perform pre or post processing.
+
+    pre: callable to be run before starting Fabric tasks
+    pre_args: a tuple of arguments to be passed to "pre"
+    pre_kwargs: a dict of keyword arguments to be passed to "pre"
+    post: callable to be run after Fabric tasks have completed on all hosts
+    post_args: a tuple of arguments to be passed to "post"
+    post_kwargs: a dict of keyword arguments to be passed to "post"
+
+    '''
+    # create a namespace to save state across hosts and tasks
+    class NS(object):
+        run_counter = 0
+
+    def true_decorator(f):
+        @wraps(f)
+        def f_wrapper(*args, **kwargs):
+            # set state variables
+            global parent_task_name
+            if not parent_task_name:
+                parent_task_name = f.__name__
+            NS.run_counter += 1
+
+            # pre-run processing
+            if f.__name__ == parent_task_name and NS.run_counter == 1: