ssh/scp commands with Sparrowdo

Sometimes you need to execute remote commands or copy files on/to remote hosts using ssh/scp commands. Here is how you can do it using Sparrowdo ssh/scp core-dsl functions.


Issuing ssh commands

The shortest form  to do this – call `ssh‘ function with minimum of required parameters – command to execute and remote host address.

ssh 'uptime', %( host => '' )

Usually people use ssh public-key authentication, so it is possible to set a path to ssh private key and provide user id:

ssh 'uptime', %(
  host    => '',
  user    => 'old-dog',
  ssh-key => 'keys/id_rsa'

Note, that ssh private key should be only stored at master host where sparrowdo runs, no other actions need to be taken, sparrowdo will care about coping(*) of ssh private key to the target host. It’s handy!

(*) By the way –  sparrowdo will remove a private ssh key from target host when ssh command is done.

There are many options of `ssh’ function you may read about at sparrowdo docs, here are just more examples.

You may run multi-line bash commands btw:

ssh q:to/CMD/, %( host => '', user => 'old_dog');
  set -e
  apt-get update
  DEBIAN_FRONTEND=noninteractive apt-get install -y -qq curl

Or don’t execute the same command twice relying on existence of file located at target server:

ssh "rm file", %(  host => '' , create => '/do/not/run/twice' );

And finally you may set alternative descriptions for your commands which will be shown at sparrowdo report to help you understand what your command does:

ssh "cat patch.sql | mysql", %(
  description => 'patching my database',
  host => ''

Issuing scp commands

`Scp’ command akin `ssh’ one.  Except it deals with remote files coping. Nothing to say here but showing some examples.

Copy a number of files to remote hosts

scp %( 
  data    => "/var/file1 /var/file2 /var/file3",
  host    => "", 
  user    => "Me", 
  ssh-key => "keys/id_rsa", 

Note, that files to copy should exists on the target hosts. If they don’t you may copy them from master host first using `file‘ function:

file '/var/file1', %( content =>  ( slurp 'files/file1' ) );
file '/var/file2', %( content =>  ( slurp 'files/file2' ) );
file '/var/file3', %( content =>  ( slurp 'files/file3' ) );

The same way as you do for `ssh’ command you may prevent from coping the same file twice if some file exists at target host:

scp %( 
  data    => "/var/biiiiiiig-file",
  host    => "", 
  create  => "/tmp/do/not/copy/me/twice"

And finally one my copy files FROM master to target host, using `pull’ flag:

scp %( 
  data    => "/var/data/dir",
  host    => "master-host:/var/file1", 
  pull    => 1, 
  ssh-key => "keys/id_rsa", 

That is it. Stay tuned with Sparrowdo Automation.  🙂

Sparrow plugins vs ansible modules


Both ansible modules and sparrow plugins are building blocks to solve elementary tasks in configuration management and automation deployment. Ansible modules are used in higher level playbooks scenarios written in YAML, sparrow plugins used in high level sparrowdo scenarios written in Perl6.

Languages support

Ansible – you may choose any language to write modules. When developing modules out of the box ansible provides seamless support for Python only ( shortcuts ), for other languages you should use third-party libraries ( natively for language you write a plugin ) to make a plugin development and integration process easier.

Sparrow – you write plugins on one of three languages – Perl5, Bash or Ruby. When developing modules sparrow provides an unified ( available for all languages ) API to make plugins development and integration easy and seamless. Though such an API as not that extensive as Python shortcuts API for ansible modules.

System design

Ansible – ansible modules are autonomous units of code to solve elementary task. Under this hood it’s just single file of code. Ansible modules can’t depend neither call other modules.

Sparrow –  sparrow plugins are very similar to ansible modules in way of autonomous, closed untis of code to solve an elementary tasks. But sparrow provides yet another level of freedom for plugin developer. Sparrow plugins actually is a  suites of scripts. Scripts may call other scripts with parameters. Such a design make it easy split even elementary task into scripts “speaking” to each other. Consider a trivial example – install / removing software packages. We can think about plugin to cope with whole elementary task ( installing / remove packages ) but under the hood split all by two scripts – one for package installing, another for package removal. This idea is quite expressed at comprehensive post – Sparrow plugins evolution.

Here is a simple illustration of what I have said.


System integration

Anisble  – ansible modules a smaller part of higher level configuration scenarios called playbooks. Ansible playbook is YAML driven dsl to declare a list of tasks – anisble modules with parameters.


– like ansible modules, sparrow plugins are smaller part of overall system – spparowdo – configuration management tool written on Perl6. Sparrowdo scenarios are Perl6 code to run a sparrow taskssparrow plugins with parameters.


End user interface

Ansible  – ansible modules gets called via playbooks using a YAML DSL to declare modules calls and pass parameters to. It is also possible to run ansible modules via command line client passing parameters as command line arguments.

Below is example of ansible playbook with ansible module yum to install httpd software package

$ cat playbook.yml
- hosts: webservers
  - name: ensure apache is at the latest version
    yum: name=httpd state=latest

Sparrow –
sparrow plugins gets called via sparrowdo scenarios using a Perl6 API. Plugin parameters gets passed as Perl6 Hashes.  Also one may use sparrow console client to run sparrow plugins as is via command line, not using  sparrowdo. There are a lot of options here – command line parameters, parameters in JSON / YMAL format, Config::General format parameters.

Below is sparrowdo equivalent to ansible module yum installing latest version of httpd.
Two flavours of API are shown – core-dsl and plugin API

$ cat sparrowfile

# you can use a short core-dsl API flavour:
package-install 'httpd'; 

# or low level plugin API flavour:
task-run 'ensure apache is at the latest version', 'package-generic', %(
   list => 'httpd'

Processing input parameters

Ansible – input parameters as key=value pairs (*), when developing plugin  you should parse an input and “split” it to the pieces of data to get a variables you need. There are plenty of “helpers” for many languages ( like Perl5, Ruby ) to simplify this process or else you have to parse input data  explicitly inside anisble module.

(*) Nested input parameters are possible

Ansible  provides a high level Python API for ansible modules called shortcuts allow you to automatically parse input and create parameter accessors,  declare parameters types, set default values, check required parameters and do other useful things with regards to.

Below is example of module parameters processing using python ansible API:

$ cat library/
from ansible.module_utils.basic import *

def main():

  fields = { "message": {"default": "Hi!", "type": "str" } }
  module = AnsibleModule(argument_spec=fields)
  # some other code here to return results
if __name__ == '__main__':  

– the similar way sparrow provides a unified ( available for all languages  ) API to access input parameters. So you don’t have to parse an input  data at all.

Thus, irrespective the language you write a plugin you get a programming API to access input parameters.  Plugin developers could define so called default configuration so that plugin input parameters ( if not set explicitly ) gets initialized with sane defaults.

Below is sparrow equivalent to the ansible module accessing named input parameter. We are going to use Bash here.

# this is plugin scenario:
$ cat story.bash
message=$(config message)

# this is default configuration:
$ cat story.ini
message = Hi!

And this is how sparrow handles nested input parameters!

$ cat sparrowfile
task-run "run my task", 'foo-plugin', %( 
 foo => { 
    bar => { 
      baz  => 'BAZ'

$ cat story.bash 

Return results

Ansible – ansible modules return results as JSON. There are some essential points  about how ansible modules return:

* an exit code of ansible module script gets ignored
* the only requirement  to module – it should print a special formatted ( containing required fields ) JSON to STDOUT
* if no valid JSON is appeared at module’s output it is considered as failure
* a STDOUT/STDERR generated by module ( if any ) is not seen at playbook output
* Thus if module developer want to return some value he/she always has to pack the data into JSON format and return it via JSON string.

Below is example of ansible module to return a current time.

$ cat library/
import datetime
import json

date = str(
print json.dumps({
    "time" : date

– sparrow plugins can return whatever , actually sparrow does not care ( but see “handle results” section ) about what is appeared at STDOUT/STDERR. There are some essential points  about how sparrow plugins returns:

* an exit code is important, it should be 0, otherwise sparrow treat a plugins execution as  failure
* a STDOUT from plugin simply gets redirected to sparrowdo output, so you always see what happening under the hood, no wrapping results into JSON is taken place like for ansible modules.

Below is sparrow equivalent to the ansible module returning a current time, we are going to use Perl5 here:

$ cat
print scalar localtime;

Handle results

Ansible – as ansible module return structured JSON data, it is possible to assign data included in JSON to some ansible variables and use them in upper level ( inside playbooks ).

Below is example of simple echo module which just return what it gets as input

$ cat playbook.yml
- hosts: localhost
    - name: tell me what I say
         message: "hi there!" 
      register: result
    - debug: var=result  

$ cat library/
from ansible.module_utils.basic import *

def main():

    module = AnsibleModule(argument_spec={})
    response = {"you_said": module.params['message']}
    module.exit_json(changed=True, meta=response)

if __name__ == '__main__':  

Sparrow – as was told sparrow  does not care about WHAT appears at plugin’s STDOUT. Well not that true. Plugins developers can defined check rules to validate STDOUT comes from plugin scripts. Such a validation consists of matching STDOUT lines against Perl regexs and many other things you can get acquainted  with at Outthenitc::DSL documentation pages – a sparrow embedded DSL to validate text output. And output validation result impact the overall execution status of sparrow plugin, thus if validation checks fails it result in failure plugin itself. Such a embedded testing facilities  make it east develop a plugins for automation testing or audit purposes.

Probably there is no to add here as example besides this dummy code 🙂

$ cat sparrowfile
run-task "tell me what I say", "echo", %( message => 'hi there!' )

$ cat story.bash
echo you said $(config message)

A trivial check rule for script output will be:

$ cat story.check
generator:  config()->{message}

Deployment process

Ansible many ansible modules gets shipped as a core part of ansible itself – ready to use, no extra efforts for deployment should be taken. Users write a custom modules and host them at SCM ( github , gitlab , svn ), finally modules are just a files get check out into directory on master host where you push ansible tasks against remote hosts, so no special actions on deployment process should be taken besides getting ansible modules files downloaded. Ansible modules eco system thus consists of three large parts:

* main Ansible repository – modules gets shipped as ansible core
* custom ansible modules

So ansible follows pure agentless schema with push approach. No modules deployment gets happened gets happened at target host. Anisble only pushes modules as files where they are executed.

Below is a schematic view of ansible custom modules deployment:


Sparrow – sparrow plugins are actually a packaged scripts gets delivered like any kind  software package – deb, rpm, rubygems, CPAN.  Sparrow exposes a console manager to download and install sparrow plugins. A sparrowdo compiles a scenarios into list of meta data and copies this into remote host. Then a sparrow manager gets run ( over ssh ) on remote host to pick up the meta data and then download, install and execute the plugins.

So sparrow follows client server schema with push approach and plugins deployments get happened on the side of target host.

Sparrow plugins have versions, ownership and documentation.  Sparrow plugins gets hosted at central plugins repository – SparrowHub

Here meta data example of sparrow plugin “package-generic” to install software packages:

    "name" : "package-generic",
    "version" : "0.2.16",
    "description": "Generic package manager. Installs packages using OS specific package managers (yum,apt-get)",
    "url" : ""

There is no rigid separation between custom and “core” plugins at sparrow eco system Every plugin gets uploaded to SparrowHub immediately becomes accessible for end users and sparrowdo scenarios. For security reasons sparrow provides ability to host so called “private”  plugins at remote git repositories. Such a plugins could be “mixed in” to standard sparrow pipeline.

Below is a schematic view of sparrow plugins deployment:



Ansible – ansible provides not built in facilities to manage dependencies at the level of ansible module, probably you would have it at level upper – ansible playbooks. Thus is you module depend on some software library you should care about such a dependency  resolution at some other place.

Sparrow – sparrow provides facilities to manage dependencies at the level of sparrow plugin.  Thus if plugin depends on software libraries you may declare such a dependencies  at the plugin scope so that plugins manager will take care about dependency resolution at the moment of plugin installing. For the time being dependencies for  Perl5 and Ruby languages are supported. CPAN modules for Perl5 via cpanfile and RubyGems for Ruby via Gemfile.


Ansible gained a big success due to extensive eco system of existed ansible modules. Though when comparing a module development process with those one exist at sparrow ( sparrow plugins ) I find some interesting and promising features a sparrow might shows at this field. To sum they up:

* Playbooks VS sparrowdo scenarios  – sparrowdo provides imperative Perl6 language interface against declarative way of ansible playbooks written in YAML. As for the some task such a declarative approach is fine, there are cases when we need add imperative style to our configuration scenarios provided by any modern generic purpose language, where  YAML for sure does not fit.

* Script oriented design – Due it’s script oriented design sparrow plugins provides you way to split a whole tasks into many simple scripts interacting with each other. This actually what we usually do when doing a regular scripting for routine tasks, so why not to bring it here? 🙂

* Modules/Plugins management and life cycle  – sparrow plugins are even more loosely coupled with configuration management tool itself then we see it at ansible. They are developed, debugged,  hosted and managed independently without even knowledge about sparrowdo configuration management tool. This makes process of plugin development more effective and less painless.

* Bash/Shell scripting –  sparrow provides much better support for  “straightforward”  bash/shell scripting then ansible due to spoken limitation of the last on return results and “JSON” interface. It is hard to understand what is going wrong in case of executing ansible bash scripts as it hides all STDOUT/STDERR generated by. Meanwhile Sparrow honestly shows what comes from executes bash/shell commands.

* Programming API – sparrow provides an unified API for all the languages, it means every language has a “equal” rights at sparrow eco system and shares the same possibilities in term of API. Meanwhile ansible modules tends to be written on Python as it seems the most seamless way to develop asnible modules.

* Testing facilities – sparrow exposes builtin test facilities which expands sparrow usage to not only deployments tasks but also to testing/monitoring/audit needs.

Sparrow plugins evolution


Black boxes and APIs.

Sparrow plugins are underlying, essential part of sparrowdo system. One the one hand they are just a scripts to solve various tasks. Like create user accounts, populate configuration files or remove directories. One other hand they are more or less black boxes with well defined API exposed to external world.

Sparrowdo uses sparrow plugins as building blocks to manage and automate remote servers. In this article I am going to give an informal introduction into sparrow/sparrowdo echo system with the focus on it’s central part, heart of all – sparrow plugins. As much as possible, I will try not to burden the material with low level technical details which might be confusing for unprepared user, however sometimes a simple code examples and diagrams will occur here, hopefully helping you to catch the main ideas and not to gets strayed.

Bottom of the system.

Well. Not to diving too much into technical aspects let me try to explain informally what sparrow plugins are. We start from the very bottom of the system, like if would not wanted know anything about sparrowdo and only wanted to play with sparrow plugins ( indeed it’s possible without sparrowdo itself! ), so these are few basic entities we have to meet first:

* Scenarios
* Stories
* Suites
* Plugins
* Tasks
* Task boxes

Every single step ahead will lead us to the whole picture of sparrow ecosystem.


Scenarios are just scripts written on one of the language of choice – Perl5, Ruby or Bash. Sparrow provide an unified, language agnostic API for script developers so they leverage it:

* Easy scripts configuration ( made in various formats – command line, Config::General,  JSON / YAML )
* Multi scripts scenarios – ability to call one script from another with parameters
* Check rules – ability verify scenarios output by embedded DSL


Picture  1. Scenarios & Stories.


Stories are abstraction for scenarios and its check rules. In terms of sparrow scenarios are always accompanied by a check list file –  a list of definitions written in a special DSL to verify script output. In trivial case it could be empty file, thus no checking is taken. If user define some patterns check lost file to validate scenario output, the verification is taken.

Say we have a simple scenario “hello world”:

$ cat
print "hello world!\n"

In sparrow scenario is executed by story executor called strun which run scripts and checks if exit code is zero, which is treated as successful status. If a check rules are supplied scenario STDOUT gets validates against such a rules. Consider a trivial check rule for the script above:

$ cat story.check
hello world

It just checks that script output contains string , there are much more things you can do when validating like providing regex checking, capturing and handling matching data and so on,  follow a Outthentic::DSL  module documentation to get more. More or less many of plugins have none or simple check rules. But if you write a monitoring / testing / audit scripts a check rules feature could be extremely useful. Another interesting idea behind check rules is “self-testing scripts“, but I am not going to talk much about this here 🙂

Ok, if we take a look at the picture number 1, we will get a visual summary of all we’ve learned so far.

Let’s go ahead and talk about sparrow suites.



Picture  2. Suites.

Suites are related stories. One may have many scripts related to a one task or split complex thing into small scripts interacting with each other. A sparrow suite always have a “main” story ( denoted as “FIRST” at the picture number 2), which “call” others in chain. So we end up a tree of stories. A story being called is “downstream” story, a story calling  downstream stories is a “upstream” story, obviously the same story could be both upstream/downstream.  When a story gets called it might be given a story parameters. A unified API is provided to handle story parameters for whatever language you choose to write a scenario. Like for Bash we could have such a code:

# upstream story
run_story my-story message 'hello world'  

# downstream story my-story.bash
message=$(story_var message)
echo $message

The same code in Perl would be:

# upstream story
run_story("my-story", { message => 'hello world' });  

# downstream story
my $message = story_var('message');
print $message;

Story parameters being passed could be nested, which are pretty good represented via Perl or Ruby hashes. And even Bash is supported ( with some limitations ):

# set parameters at upstream story:

# Perl
run_story("S1", { message => { hello => 'world' } } );

# Ruby
run_story "S1", { :message => { :hello => 'world' } }  

# Bash
run_story message.hello world

# access parameters at downstream story:

# Perl

# Ruby

# Bash
$(story_var message.hello )

A technical details on sparrow stories could be found at Outthentic module documentation.

The same way as stories accept parameters and handle them using unified API, one may configure a sparrow suites. Say we want to pass some global parameters as suite input. Lets create first a default suite configuration, it could be ( one of the options, see later ) a Config::General format file:

$ cat suite.ini

         nginx 80
         tomcat 8080
         dev_server 3000

$ strun --ini suite.ini

Now we have a unified API to access global parameters:

# access global parameters at story:
# Perl
# Ruby 
# Bash 
$(config app.servers_and_ports.nginx )

We even can override a suite global parameters at run time via command line:

$ strun --param app.servers_and_ports.nginx=81

And finally we could use a JSON/YAML format to store global parameters:

$ cat suite.json

  "app": {
   "servers_and_ports": {
     "nginx" : 80,
     "tomcat" : 8080
     "dev_server": 3000

$ strun --json suite.json

Default configuration and Hash merge.

If we have suite.ini configuration file for our suite it is considered as default configuration file. Thanks to Hash::Merge   it is possible to override ( merge two files into one Hash ) a default values using custom configuration file:

$ cat suite.ini

  bar = bar-default-value
  baz = baz-default-value

$ strun --ini suite.ini # load a default configuration

$ cat
  bar = bar-new-value

$ strun --ini 

# will override bar value to `bar-new-value`
# baz value will remain default.

To know more about suites and their interfaces take a look at Outthentic documentation –

Now let’s see how suites becomes a plugins.

Sparrow plugins

Picture 3. Sparrow plugins distribution system.

Plugins are packaged suites ready for contribution. From the end user point of view plugins act as suites, so they “borrowed” all the features we have learned so far.

But there are some extra values plugins add into the system:

* name and version
* dependencies ( CPAN / RubyGems )
* ownership

Every plugin has a name to be identified in a global sparrow system. This is obvious like having names for every software packages. As well as plugins have a versions so plugin developer may release and plugin users may utilize a various versions of plugins:

$ sparrow plg search nginx # search nginx related plugins
$ sparrow plg install nginx-check # install nginx-check plugin
$ sparrow plg run nginx-check # run nginx-check plugin
$ sparrow plg run nginx-check --version 0.0.8 # installing by version

Sparrow has quite extensive API on managing plugins, we can’t focus on here, please follow a documentation if you are interested. What is important here that plugins are small bits of software which is distributes the same as we see at many package systems like apt, CPAN, rpm, RubyGems so on.


In a spirit of ansible modules sparrow plugins does not depend on other plugins, but we can use any software libraries in our scenarios. Currently a plugin developer can declare CPAN dependence in a cpanfile or RubyGems dependencies in Gemfile, so that such a dependencies will be installed. Sparrow adjusts running environment ( setting library paths for Perl and Ruby ) so that installed libraries will be accessible in running scenario. It’s very handy!


To publish plugins into central repository SparrowHub you need to get account there. It is also possible to distribute so called private plugins hosted at remote git repositories.


All spoken could be written in simple JSON format. This is how sparrow plugins get registered in sparrow system:

  "version" : "",
  "name"    : "nginx-check",
  "description" : "checks if nginx server is healthy by executing low level system checks ( ps, pid, etime )",
  "url"         : ""

Sparrow tasks

Sparrow plugins bind to a default suite configuration, there is not that much you can do about it, only redefine global parameter at run time:

$ sparrow plg run foo --param a=1 --param b=2

sparrow-plugins-tasksPicture 4. Sparrow plugins and tasks.

Sparrow tasks give you way more agile. Tasks are plugins with custom configurations.  Tasks have names and grouped by projects:

$ sparrow task add foo-project foo-task foo
$ sparrow task ini foo-project/foo-task
a = 100
b = 200
$ sparrow task run foo-project/foo-task

There a lot of information about sparrow task at Sparrow documentation pages.

Ok, it’s been a long trip. We are approaching the end of evolution here 🙂 And this is sparrow task boxes.

Task box

Task box is a collection of sparrow tasks, we can write it as JSON:

    "task" : "foo-task",
    "plugin" : "foo-plugin",
    "global_parameters" : {
       "a" : 1,
       "b" : 2
    "task" : "bar-task",
    "plugin" : "bar-plugin",
    "global_parameters" : {
      "aa" : 1,
      "bb" : 2

Sparrow tasks is way to run many sparrow plugins with parameters and consequently. This actually what Sparrowdo does when compiling sparrowdo scenarios:

$ cat sparrowfile
user "zookeeper";directory "/var/data/zoo";
file "/var/data/zoo/birds.txt", %( owner => 'zookeeper' );

A given code gets complied into sparrow task box:

     "plugin" : "user", 
     "task" : "create user zookeeper", 
     "data" : { "name" : "zookeeper", "action" : "create" } 
     "plugin" : "directory", 
     "task" : "create directory /var/data/zoo", 
     "data" : { "path" : "/var/data/zoo", "action" : "create" } 
     "plugin" : "file", 
     "task" : "create file /var/data/zoo/birds.txt", 
     "data" : { 
        "owner" : "zookeeper", 
        "action" : "create", 
        "target" : "/var/data/zoo/birds.txt" 

From the very bottom of the system we have reached a sparrow evolution end point – a high level configuration management scenarios written on Perl6. But under the hood – it’s just a JSON gets pushed to sparrow client, do it will do low level job by executing sparrow plugins 🙂 , see the last picture:


Picture 5. Sparrow plugins evolution.


Let’s summarize what we’ve learned in this article:

* Sparrow plugins are scripts written on one of language of choice: Perl5/Bash/Ruby
* Outthentic – a core sparrow component – a development and execution kit to enable some frequently used  features when writing automation scenarios: testing script output , reuse other scripts and pass script configuration parameters.
* To distribute scripts they are packaged and uploaded into central repository – SparrowHub
* Sparrow client is command line tool to install, configure and run plugins.
* Sparrowdo acts as high level system build upon sparrow plugins to write an automation scenarios in Perl6 language and then execute them as sparrow “plugins-primitives” with the JSON as internal presentation format and scp/ssh as transport.

I hope this was a helpful article, please post your comments, questions, ideas here.


— Alexey Melezhik

Sparrowdo reports

Finally I have succeeded in suitable sparrowdo reports format, at least it looks fine for me, 🙂

Consider installing CPAN modules scenario:

user-create 'foo';
cpan-package-install ('CGI', 'Config::Tiny', 'HTTP::Tiny'), %(
    user =>'foo',
    install-base => '/home/foo/',

Now sparrowdo report for it looks like:

{create user foo} modules/create/
  useradd: user 'foo' already exists
  uid=1002(foo) gid=1002(foo) groups=1002(foo)
  user created
  ok      scenario succeeded
  ok      text has 'user created'

{create user foo}
  ok      text has 'done'

{install cpan packages: CGI Config::Tiny HTTP::Tiny}
  @ CPAN package installer

{install cpan packages: CGI Config::Tiny HTTP::Tiny} modules/cpanm/ params: package:CGI
  install CGI into /home/foo/ user foo ...
  CGI is up to date. (4.35)
  install ok
  ok      scenario succeeded
  ok      text has 'install ok'

{install cpan packages: CGI Config::Tiny HTTP::Tiny} modules/cpanm/ params: package:Config::Tiny
 install Config::Tiny into /home/foo/ user foo ...
 Config::Tiny is up to date. (2.23)
 install ok
 ok      scenario succeeded
 ok      text has 'install ok'

{install cpan packages: CGI Config::Tiny HTTP::Tiny} modules/cpanm/ params: package:HTTP::Tiny
 install HTTP::Tiny into /home/foo/ user foo ...
 HTTP::Tiny is up to date. (0.070)
 install ok
 ok      scenario succeeded
 ok      text has 'install ok'

Here we find a pattern for report blocks:

{ task description, story path, story parameters }
{ @ story description  }
… scenario output  …
… scenario output  …
… scenario output  …
check-status check-message # first one
check-status check-message  # second one
check-status …
check-status check-message # last one

Well … story path, story parameters, story description and check statuses are optional and might not appear at reports, but you always get a task description, scenario-status and final task status.

And finally for those who like pictures here is colorful sparrowdo report gets shapshoted from my console:


Sparrowdo system design

Sparrowdo comprises of three main components.

* server part – a sparrowdo client, should be installed at master host. Sparrowdo compiles sparrowdo scenarios written on Perl6 into json data. Then json data gets copied to the target host by scp command  and sparrow client gets run remotely on the target host with json data as input parameters.

* client part – a sparrow client, should be installed at target hosts where you run automation deployment and configuration tasks. Sparrow client gets run over ssh at the target host, then installs and finally executes a so called sparrow plugins with parameters gets ready as json data copied heretofore by sparrowdo.

* sparrow plugins – multipurpose scenarios written on one of the language of your choice- Perl5, Bash or Ruby. Sparrow plugins get downloaded from and then installed by sparrow. Plugins get executed with input parameters comes from json data copied from master host to target server.

Thus, sparrowdo follows a “push” paradigm when there is central ( master ) server where you invoke ( push ) some tasks remotely ( over ssh ) on remote hosts. Below is visual presentation of the system design:


So, sparrowdo is a client sever system, with push approach similar to ansible. To allow full automation sparrowdo comes with –bootstrap(*) option to install sparrow client on target host:

$ sparrowdo –bootstrap –host=

(*) bootstrap currently only available for CentOS target sever platform.

Read more about this at sparrowdo documentation pages –

Sparrowdo core dsl

Sparrowdo core-dsl is end user interface to easily start automation. Let me show a few examples:

# create / remove user:

user-create 'zookeeper';
user-delete 'zookeeper'

# create / remove group:

group-create 'birds';
group-delete 'birds';

# install packages:

package-install ('nano', 'tree', 'mc');

# install CPAN packages:

cpan-package-install ('CGI', 'Config::Tiny', 'HTTP::Tiny')

# manage services:

service-start 'nginx';
service-enable 'nginx';
service-restart 'nginx';

# create / delete directories:

directory-create '/var/data/feathered', %( 
   recursive => 1, 
   owner => 'zookeeper' 
directory-delete '/var/data/feathered';

# create files and templates:

file '/var/data/feathered/sparrow-greeting.txt', %( 
   mode    => '644', 
   owner   => 'zookeeper',
   content => 'hello! my name is Brave Sparrow'   
# Template-Toolkit templates are supported:
$ cat templates/sparrow-greeting.tmpl
my name is %name%
I can speak %langauge%
template-create '/var/data/feathered/sparrow-greeting.txt', %( 
    source => ( slurp 'examples/templates/sparrow-greeting.tmpl' ), 
    owner => 'zookeeper', 
    group => 'birds', 
    mode => '644', 
    variables => %( 
        name => 'Brave Sparrow', 
        language => 'English' 

To know more on core-dsl follow sparrowdo documentaion pages –