github.com/jbking/gohan@v0.0.0-20151217002006-b41ccf1c2a96/docs/source/extension.rst (about)

     1  ==============
     2  Extension
     3  ==============
     4  
     5  You can add additional logic using Gohan Extension.
     6  Extensions has properties:
     7  
     8  - id identity of the code
     9  - code contents of code
    10  - code_type javascript, go and donburi (DSL) are supported
    11  - url placement of code. currenty, file://, http:// and https:// schemes are supported
    12  - path resource path to execute code
    13  
    14  Example Code
    15  
    16  .. code-block:: yaml
    17  
    18    extensions:
    19    - code: console.log(Object.keys(context));
    20      id: test
    21      path: /v2.0/.*
    22  
    23  Javascirpt Code block
    24  ---------------------
    25  
    26  In the gohan extension code, you need to register context using
    27  gohan_register_handler function.
    28  gohan_register_handler talkes event_type (string)_ and handler (function(context)).
    29  
    30  .. code-block:: javascript
    31  
    32    gohan_register_handler("pre_show", function(context){
    33      context.resp = "pre_show event"
    34    });
    35  
    36    gohan_register_handler("pre_update", function(context){
    37      context.resp = "pre_update event "
    38    });
    39  
    40  context has following items
    41  
    42    context.schema : schema information
    43    context.path : url path
    44    context.role : user role
    45    context.auth : auth_context information
    46    context.http_request : Go HTTP request object
    47    context.http_response : Go HTTP response writer object
    48  
    49  
    50  Build in exception types
    51  ------------------------
    52  
    53  In an effort to simplify writing extensions for validation Gohan supports
    54  throwing some exceptions and handles them internally.
    55  Gohan provides the following exception types.
    56  
    57  - BaseException(msg)
    58  
    59  The base type of exception, should never be raised as itself, only extended.
    60  
    61  - CustomException(msg, code)
    62  
    63  A BaseException with an additional code property. When thrown will result in
    64  an http response with the provided code and message being written.
    65  
    66  One can extend the CustomException. An example follows.
    67  
    68  .. code-block:: javascript
    69  
    70    function ValidationException(msg) {
    71      CustomException.call(this, msg, 400);
    72      this.name = "ValidationException";
    73    }
    74    ValidationException.prototype = Object.create(CustomException.prototype);
    75  
    76  
    77  .. _`gohan built in functions`:
    78  
    79  Build in javascript functions
    80  -----------------------------
    81  
    82  Gohan extension supports some build-in functions.
    83  
    84  - console.log(string)
    85  
    86  Logging output
    87  
    88  - gohan_http(method, url, headers, data, options)
    89  
    90  fetch data from url
    91  method : GET | POST | PUT | DELETE
    92  url : destination url
    93  headers : additional headers (eg. AUTH_TOKEN)
    94  data : post or put data
    95  options : dictionary of options for http client
    96  opaque : boolean - whether to parse URL or to treat it as raw
    97  
    98  - gohan_db_list(transaction, schema_id, filter_object)
    99  
   100  retrive all data from database
   101  
   102  - gohan_db_fetch(transaction, schema_id, id, tenant_id)
   103  
   104  get one data from db
   105  
   106  - gohan_db_query(transaction, schema_id, query_string, arguments)
   107  
   108    Retrieve data with a raw query
   109  
   110    - transaction: The transaction to use. When null will use a new one.
   111    - schema_id: The ID of the schema of which the query result populates instances
   112    - query_string: Raw query string such as a SQL SELECT query
   113  
   114      - You can put a "?" as the placeholder of variables in your query string
   115  
   116    - arguments: An array of actual values that replace place holders in query_string
   117  
   118  - gohan_db_create(transaction, schema_id, object)
   119  
   120  create data in db
   121  
   122  - gohan_db_update(transaction, schema_id, object)
   123  
   124  update data in db
   125  
   126  - gohan_db_state_update(transaction, schema_id, object)
   127  
   128  update data in db without informing etcd
   129  
   130  - gohan_db_delete(transaction, schema_id, object)
   131  
   132  delete data in db
   133  
   134  - gohan_model_list(context, schema_id, filter)
   135  
   136    Retrieve data through Gohan.
   137  
   138    - context: You need to have transaction in this dictionary which you can get from given context
   139    - schema_id: The id of the schema of the objects we want to retrieve.
   140    - filter: How to filter retrieved objects. Should be a dictionary with each key being either:
   141  
   142      - A property of the schema we are retrieving. Then the value has to either be a string or an array of strings.
   143        The response is then filtered by removing all entries that do not have the value for the given key in the provided array.
   144      - Any of the strings 'sort_key', 'sort_order', 'limit', 'offset'. These are interpreted with their values as query parameters.
   145  
   146  - gohan_model_fetch(context, schema_id, resource_ids)
   147  
   148    Retrieve a specific resource through Gohan.
   149  
   150    - context: You need to have transaction in this dictionary which you can get from given context
   151    - schema_id: The id of the schema of the object we want to retrieve.
   152    - resource_id: The id of the object we want to retrieve.
   153    - tenant_ids: allowed tenant id
   154  
   155  - gohan_model_create(context, schema_id, data)
   156  
   157    Create an object through Gohan.
   158  
   159    - context: You need to have transaction in this dictionary which you can get from given context
   160    - schema_id: The id of the schema of the object we want to create.
   161    - data: The data needed to create the object, in the form of a dictionary.
   162  
   163  - gohan_model_update(context, schema_id, resource_id, data, tenant_ids)
   164  
   165    Update an object through Gohan.
   166  
   167    - context: You need to have transaction in this dictionary which you can get from given context
   168    - schema_id: The id of the schema of the object we want to update.
   169    - resource_id: The id of the object we want to update.
   170    - data: The data needed to update the object, in the form of a dictionary.
   171    - tenant_ids: allowed tenant id
   172  
   173  - gohan_model_delete(context, schema_id, resource_id)
   174  
   175    Delete an object through Gohan.
   176  
   177    - context: You need to have transaction in this dictionary which you can get from given context
   178    - schema_id: The id of the schema of the object we want to delete.
   179    - resource_id: The id of the object we want to delete.
   180  
   181  - gohan_schemas()
   182  
   183  returns all registered schemas
   184  
   185  - gohan_schema_url(schema)
   186  
   187  returns the url for the schema
   188  
   189  - gohan_policies()
   190  
   191  returns all policies
   192  
   193  - gohan_uuid()
   194  
   195  generate uuid v4
   196  
   197  - gohan_sleep(time)
   198  
   199  sleep time (ms)
   200  
   201  - gohan_execute(comand_name, args)
   202  
   203  execute shell command
   204  
   205  - gohan_template(template_string, variables)
   206  
   207  apply go style template
   208  
   209  - gohan_netconf_open(hostname, username)
   210  
   211  open netconf session.
   212  (Note: you need set up ssh key configuraion
   213  on both of gohan and target node.)
   214  In gohan, you need to setup ssh/key_file
   215  configuraion.
   216  
   217  - gohan_netconf_exec(session, command)
   218  
   219  execute netconf command
   220  
   221  - gohan_netconf_close(session)
   222  
   223  close netconf session
   224  
   225  - gohan_ssh_open(hostname, username)
   226  
   227  open ssh session.
   228  (Note: you need set up ssh key configuraion
   229  on both of gohan and target node.)
   230  In gohan, you need to setup ssh/key_file
   231  configuraion.
   232  
   233  - gohan_ssh_exec(session, command)
   234  
   235  execute command on ssh session
   236  
   237  - gohan_ssh_close(session)
   238  
   239  close ssh session
   240  
   241  - require(module)
   242  
   243  Dynamically load modules
   244  
   245  .. _event:
   246  
   247  Event
   248  ----------------
   249  
   250  - pre_list
   251  
   252    list event before db operation
   253  
   254    context.response contains response data.
   255    You can also update response here
   256  
   257    Note you can skip db operation if you set context response in here
   258  
   259  - pre_list_in_transaction
   260  
   261    same as pre_list but executed in the db transaction
   262    context.transaction contains transaction object for db operation
   263  
   264    id : request id
   265    context.response contains response data.
   266    You can also update response here
   267  
   268  - post_list_in_transaction
   269  
   270    same as post_list but executed in the db transaction
   271    context.transaction contains transaction object for db operation
   272  
   273    id : request id
   274    context.response contains response data.
   275    You can also update response here
   276  
   277  - post_list
   278  
   279    list event after db operation.
   280  
   281    context.response contains response data.
   282    You can also update response here
   283  
   284  - pre_show
   285  
   286    show event before db access
   287  
   288    id : request id
   289    context.response contains response data.
   290    You can also update response here
   291  
   292    Note you can skip db operation if you set context response in here
   293  
   294  - pre_show_in_transaction
   295  
   296    same as pre_show but executed in the db transaction
   297    context.transaction contains transaction object for db operation
   298  
   299    id : request id
   300    context.response contains response data.
   301    You can also update response here
   302  
   303  - post_show_in_transaction
   304  
   305    same as post_show but executed in the db transaction
   306    context.transaction contains transaction object for db operation
   307  
   308    id : request id
   309    context.response contains response data.
   310    You can also update response here
   311  
   312  - post_show
   313  
   314    show event after db operation
   315  
   316    id : request id
   317    context.response contains response data.
   318    You can also update response here
   319  
   320  - pre_create
   321  
   322    executed before creation
   323    Mainly used for validation purpose
   324  
   325    context.resource contains user input data
   326  
   327    Note you can skip db operation if you set context response in here
   328  
   329  - pre_create_in_transaction
   330  
   331    same as pre_create but executed in the db transaction
   332    context.transaction contains transaction object for db operation
   333  
   334  - post_create_in_transaction
   335  
   336    after creation in transaction
   337  
   338  - post_create
   339  
   340    after create
   341    context.response contains response data.
   342    context.transaction contains transaction object for db operation
   343  
   344  - pre_update
   345  
   346    executed before update
   347    Mainly used for validation purpose
   348  
   349    context.resource contains user input data
   350  
   351    Note you can skip db operation if you set context response in here
   352  
   353  - pre_update_in_transaction
   354  
   355    same as pre_update but executed in the db transaction
   356    context.transaction contains transaction object for db operation
   357  
   358  - post_update_in_transaction
   359  
   360    after creation in transaction
   361  
   362  - post_update
   363  
   364    after update
   365    context.response contains response data.
   366    context.transaction contains transaction object for db operation
   367  
   368  - pre_delete
   369  
   370    executed before delete
   371    Mainly used for validation purpose
   372  
   373    context.id contains resource id we are trying to delete
   374    context.transaction contains transaction object for db operation
   375  
   376  - pre_delete_in_transaction
   377  
   378    same as pre_delete but executed in the db transaction
   379    context.transaction contains transaction object for db operation
   380  
   381  - post_delete_in_transaction
   382  
   383    after creation in transaction
   384  
   385  - post_delete
   386  
   387    after delete
   388  
   389  - pre_state_update_in_transaction
   390  
   391    executed before a state update triggerred by a backend event
   392  
   393    context.resource contains the resource associated with the update,
   394    context.state contains the state changes,
   395    context.config_version contains the current config version
   396  
   397  - post_state_update_in_transaction
   398  
   399    as above, but after the state update
   400  
   401  - pre_monitoring_update_in_transaction
   402  
   403    executed before a monitoring update triggerred by a backend event
   404  
   405    context.resource contains the resource associated with the update,
   406    context.monitoring contains the new monitoring information
   407  
   408  - post_monitoring_update_in_transaction
   409  
   410    as above, but after the monitoring update
   411  
   412  - notification
   413  
   414    executed when you receive amqp/snmp/cron notification
   415  
   416  Testing javascript extensions
   417  -----------------------------
   418  
   419  You can test extensions using a testing tool bundled with Gohan through new
   420  command ``testextensions`` (or simply ``te``). Build and install Gohan, then
   421  run ``gohan testextensions <paths to files/directories to test>``. The
   422  framework will walk through files and recursively through directories, matching
   423  files named ``test_.*.js`` and running tests.
   424  
   425  Test file contents
   426  ^^^^^^^^^^^^^^^^^^
   427  Each test file must specify schema and path for preloading extensions:
   428  
   429  * var SCHEMA - path to the schema that stores extensions to be tested
   430  * var PATH - path for preloading extensions
   431  
   432  Additionally each file can specify:
   433  
   434  * one setUp() function that will be called before each test
   435  * one tearDown() function that will be called after each test
   436  * multiple test_<name>() functions that will be called by the framework
   437  * multiple helper functions and variables, with names not starting with prefix
   438    ``test_``
   439  
   440  Framework API
   441  ^^^^^^^^^^^^^
   442  Test framework provides all built in function mentioned in subsection
   443  describing `gohan built in functions`_.
   444  
   445  To avoid making HTTP requests during tests, ``gohan_http`` function is a mock.
   446  You can pass values that will be returned for given arguments during subsequent
   447  calls by calling ``gohan_http.Expect(argument, ...).Return(value)``. One call to
   448  ``gohan_http.Expect(arguments, ...).Return(value)`` provides one response of
   449  ``gohan_http`` (FIFO queue). If no return value, or wrong arguments are provided
   450  for a call then an unexpected call is assumed, which will result in test failures.
   451  
   452  In addition to the abovementioned functions, the framework provides the
   453  following API:
   454  
   455  * ``Fail(format_string, ...)`` - stop execution of a single test case and
   456    return an error
   457  
   458  * ``GohanTrigger(event_type, context) : <new context>`` - triggers a specified
   459    type of Gohan event
   460  
   461    * ``event_type`` - one of the event types recognized by Gohan (see
   462      event_ subsection)
   463  
   464    *  ``context`` - context passed to the event handler
   465  
   466  * ``MockTransaction() : <mock transaction>`` - return a mock transaction that
   467    can be used with built-in Gohan methods. Each test is run using a separate
   468    database that is deleted after ``tearDown()``, so there is no need to
   469    clean up the database between tests. Multiple calls to ``MockTransaction()``
   470    within a single ``setUp()``, test, ``tearDown()`` routine when no call to
   471    ``CommitMockTransaction()`` has been made will yield the same transaction.
   472  
   473  * ``CommitMockTransaction()`` - commit and close the last nonclosed
   474    transaction. After this call any calls to ``MockTransaction()`` return
   475    a new transaction.
   476  
   477  * ``MockPolicy() : <mock policy>`` - return a mock policy that
   478    can be used with built-in Gohan methods.
   479  
   480  * ``MockAuthorization() : <mock authorization>`` - return a mock authorization that
   481    can be used with built-in Gohan methods.
   482  
   483  Example
   484  ^^^^^^^
   485  A sample test may look like this:
   486  
   487  .. code-block:: javascript
   488  
   489    // Schema file containing extensions to be tested
   490    var SCHEMA = "../test_schema.yaml";
   491  
   492    /**
   493     * Sample contents of test_schema.yaml:
   494     *
   495     * extensions:
   496     * - id: network
   497     *   path: /v2.0/network.*
   498     *   url: file://./etc/examples/neutron/network.js
   499     * - id: exceptions
   500     *   path: ""
   501     *   url: file://./etc/examples/neutron/exceptions.js
   502     * - id: urls
   503     *   path: /gohan/v0.1/schema.*
   504     *   url: file://./etc/examples/url.js
   505     * schemas:
   506     * - description: Network
   507     *   id: network
   508     *   parent: ""
   509     *   plural: networks
   510     *   schema:
   511     *     properties:
   512     *       id:
   513     *         format: uuid
   514     *         permission:
   515     *         - create
   516     *         title: ID
   517     *         type: string
   518     *         unique: true
   519     *       tenant_id:
   520     *         format: uuid
   521     *         permission:
   522     *         - create
   523     *         title: Tenant id
   524     *         type: string
   525     *         unique: false
   526     *     propertiesOrder:
   527     *     - name
   528     *     - id
   529     *     - tenant_id
   530     *   singular: network
   531     *   title: Network
   532     */
   533  
   534    // With the following PATH, "network" and "exceptions" extensions will be loaded
   535    var PATH = "/v2.0/networks";
   536  
   537    /**
   538     * Sample contents of network.js:
   539     *
   540     * // filter removes the network with the unwanted id
   541     * gohan_register_handler("post_list", function filter(context) {
   542     *     // This call will be mocked, see testNetworkListFilter below
   543     *     response = gohan_http("GET", "http://whatisunwanted.com", {}, null);
   544     *
   545     *     for (var i = 0; i < context.response.networks.length; i++) {
   546     *         if (context.response.networks[i].id == response.unwanted) {
   547     *             context.response.networks.splice(i, 1);
   548     *             break;
   549     *         }
   550     *     }
   551     * });
   552     */
   553  
   554    var context;
   555    var network;
   556  
   557    function setUp() {
   558        var network_to_create = {
   559            'id': 'new',
   560            'tenant_id': 'azerty'
   561        };
   562        network = gohan_db_create(MockTransaction(), "network", network_to_create);
   563        context = {
   564            'schema': { /* ... */ },
   565            'http_request': { /* ... */ },
   566            'http_response': { /* ... */ },
   567            'path': '/gohan/v0.1/schema',
   568            'response': {
   569                'networks': [
   570                    network,
   571                    {
   572                        'id': 'foo',
   573                        'tenant_id': 'xyz'
   574                    }
   575                 ]
   576             }
   577        }
   578    }
   579  
   580    function tearDown() {
   581      gohan_db_delete(MockTransaction(), "network", "new");
   582    }
   583  
   584    function testNetworkListFilter() {
   585        // First call to gohan_http will return {'unwanted': 'foo'}
   586        gohan_http.Expect("GET", "http://whatisunwanted.com", {}, null).Return({'unwanted': 'foo'});
   587        // Second call to gohan_http will return empty response
   588        gohan_http.Expect("GET", "http://whatisunwanted.com", {}, null).Return({});
   589        // Subsequent calls to gohan_http will fail since they are not expected
   590        var new_context = GohanTrigger('post_list', context);
   591  
   592        if (new_context.response.networks.length != 1) {
   593           Fail('Expected 1 network but %d found.', new_context.response.networks.length);
   594        }
   595  
   596        if (new_context.response.networks[0].id != network.id) {
   597           Fail('Expected network with id "%s" but "%s" found.', network.id, new_context.response.networks[0].id);
   598        }
   599    }
   600  
   601    function testSomethingElse() {
   602        /* ... */
   603    }
   604  
   605  Javascript Backend
   606  -------------------------
   607  
   608  Currenly, gohan is using Otto. Otto is a pure golang implementation
   609  for javascript.
   610  Gohan also have experimental support for v8. v8 runs js code 100-1000 times faster than Otto.
   611  
   612  TODOs
   613  - no build-in are implemented yet
   614  
   615  In order to make v8 version of Gohan, you need v8worker https://github.com/ry/v8worker installed in your env. (see more instruction on the repository).
   616  
   617  In order to enable v8 support on extension. then set ENABLE_V8=true
   618  
   619  .. code-block:: shell
   620  
   621    ENABLE_V8=true make
   622  
   623  
   624  Go based extension
   625  -------------------------
   626  
   627  You can extend gohan extension by native go.
   628  You can use "go" for code_type and specify your callback id in code.
   629  Also, you can register go struct & call it from javascript.
   630  
   631  .. code-block:: yaml
   632  
   633    extensions:
   634    - code: exampleapp_callback
   635      code_type: go
   636      id: example
   637      path: .*
   638    - code: exampleapp_callback
   639      code_type: go
   640      id: example
   641      path: .*
   642    - code: |
   643        gohan_register_handler("pre_list", function (context){
   644          var exampleModule = require("exampleapp");
   645          exampleModule.HelloWorld("example app!",
   646            {"hobby": "sleeping"});
   647        });
   648      id: example_js
   649      path: .*
   650  
   651  
   652  .. code-block:: go
   653  
   654    //Register go callback
   655    extension.RegisterGoCallback("exampleapp_callback",
   656    	func(event string, context map[string]interface{}) error {
   657    		fmt.Printf("callback on %s : %v", event, context)
   658    		return nil
   659    })
   660  
   661    exampleModule := &ExampleModule{}
   662    //Register go based module for javascript
   663    extension.RegisterModule("exampleapp", exampleModule)
   664  
   665  
   666  We have exampleapp with comments in exampleapp directory.
   667  You can also, import github.com/cloudwan/server module and
   668  have your own RunServer method to have whole custom route written in go.
   669  
   670  
   671  Donburi
   672  -------------------------
   673  
   674  Note: This function is experimental. Any APIs are subject to change.
   675  
   676  Gohan support Donburi which is a yaml based DSL to support extension.
   677  Donburi is heavyly inspired by Ansible yaml script.
   678  The goal of Donburi is pain-less extension using YAML.
   679  This is donburi example.
   680  
   681  .. code-block:: yaml
   682  
   683    db_tasks:
   684      - list:
   685          schema_id: "test"
   686          tenant_id: "xxx"
   687        register: gohan_db
   688      - fetch:
   689          schema_id: "test"
   690          id: "xxx"
   691      - resource:
   692          id: "xxx"
   693          schema: "test"
   694          properties:
   695            name: "test"
   696        register: "xxx"
   697    tasks:
   698      - vars:
   699          message: world
   700      - debug: "hello {{ .message }} "
   701      - eval: "console.log(id);"
   702      - contrail:
   703          schema: "virtual_networks"
   704          properties:
   705            name: "test"
   706        register: vm1_out
   707  
   708  
   709  This is the other sample.
   710  
   711  .. code-block:: yaml
   712  
   713    tasks:
   714      - eval: "1 + 1"
   715        register: result
   716      - eval: "true"
   717        register: when_is_working
   718        when: "result == 2"
   719      - block:
   720        - vars:
   721            list2 : [4, 5, 6]
   722        - eval: "result += item"
   723          with_items:
   724           - 1
   725           - 2
   726           - 3
   727        when: when_is_working
   728      - eval: "result += item"
   729        with_items: "list2"
   730  
   731  
   732  you can find an example application at etc/appts/donburi.yaml, and
   733  example server configuraion in etc/donburi.yaml.
   734  
   735  Example Application
   736  ^^^^^^^^^^^^^^^^^^^
   737  
   738  - Setup contrail + openstack using vagrant
   739  
   740  See https://github.com/mwiget/opencontrail
   741  
   742  - Setup CORS (Cross-Origin Resource Sharing)  on keystone
   743  
   744  See https://ianunruh.com/2014/11/openstack-cors.html
   745  
   746  - Setup notification configuration on heat
   747  
   748  /etc/heat/heat.conf
   749  
   750  notification_driver=heat.openstack.common.notifier.rpc_notifier
   751  
   752  restart heat-api and heat-engine
   753  
   754  - Allow rabbitmq connection from your gohan host
   755  
   756  Example
   757  
   758  .. code-block:: shell
   759  
   760    root@ubuntu-14:/etc/rabbitmq# cat rabbitmq.config
   761    [
   762       {rabbit, [ {tcp_listeners, [{"0.0.0.0", 5672}]},
   763       {loopback_users, []},
   764       {log_levels,[{connection, info},{mirroring, info}]} ]
   765  
   766  
   767    root@ubuntu-14:/etc/rabbitmq# cat rabbitmq-env.conf
   768  
   769    NODE_IP_ADDRESS=0.0.0.0
   770    NODENAME=rabbit@ubuntu-14-ctrl
   771  
   772  restart rabbitmq
   773  
   774  - Update keystone configuraion on etc/donburi.yaml
   775  
   776  keystone:
   777      use_keystone: true
   778      fake: false
   779      auth_url: "http://172.16.25.130:5000/v3"
   780      user_name: "admin"
   781      tenant_name: "admin"
   782      password: "secret123"
   783      version: v3
   784  
   785  - Start gohan
   786  
   787  gohan server --config-file etc/donburi.yaml
   788  
   789  
   790  Variables
   791  ^^^^^^^^^^^^^^^^^^^
   792  
   793  You can register variables using vars task
   794  
   795  .. code-block:: yaml
   796  
   797      - vars:
   798        list2 : [4, 5, 6]
   799  
   800  you can use values in context in each value using golang
   801  template format.
   802  (see more details on http://golang.org/pkg/text/template/ )
   803  
   804  For example, you can use context.tenant value using
   805  "{{ .tenant }}""
   806  
   807  Note that template isn't allowed in eval and when.
   808  
   809  Block
   810  ^^^^^^^^^^^^^^^^^^^
   811  
   812  You can have a logical grouping of tasks.
   813  We have "block" and "resources".
   814  
   815  
   816  .. code-block:: yaml
   817  
   818    - block:
   819      - vars:
   820          list2 : [4, 5, 6]
   821      - eval: "result += item"
   822        with_items:
   823         - 1
   824         - 2
   825         - 3
   826      when: when_is_working
   827  
   828  If you use reousrces block, each sub task will be executed
   829  on reverse order on resource deletion.
   830  
   831  .. code-block:: yaml
   832  
   833    - resources:
   834      - resource A
   835      - resource B # depends on resource A
   836  
   837  Define
   838  ^^^^^^^^^^^^^^^^^^^
   839  
   840  You can define a function using "define" block.
   841  last task execution result will be returned.
   842  
   843  .. code-block:: yaml
   844  
   845    - define:
   846      name: add
   847      tasks:
   848      - eval: "a + b"
   849    - add:
   850        a: 1
   851        b: 2
   852      register: c
   853  
   854  
   855  Event handling
   856  ^^^^^^^^^^^^^^^^^^^
   857  
   858  db_tasks  will be executed in main transaction
   859  tasks will be executed outside of transaction
   860  
   861  Conditionals
   862  ^^^^^^^^^^^^^^^^^^^
   863  
   864  when: the statement specifed in "when" will be evaluated, and when it is true
   865  the task will be executed
   866  else: if evaluation result of "when" get false, else will be executed
   867  
   868  .. code-block:: yaml
   869  
   870    tasks:
   871      - debug: "result is 2"
   872        when: "result == 2"
   873        else:
   874          - debug: "result is not 2"
   875  
   876  Error handling
   877  ^^^^^^^^^^^^^^^^^^^
   878  
   879  You can use rescue and always block task for error handling.
   880  rescue will be executed only if we got execption.
   881  always will be executed always.
   882  retry (int) block, rescue, always will be executed "retry" count
   883  times.
   884  
   885  .. code-block:: yaml
   886  
   887    tasks:
   888      - block:
   889        - eval: throw 'error'
   890        rescue:
   891        - eval: "rescue_executed = true"
   892        always:
   893        - eval: "always_executed = true"
   894        retry: 3
   895  
   896  
   897  Note that last error will be stored in context.error
   898  
   899  Loops
   900  ^^^^^^^^^^^^^^^^^^^
   901  
   902  with_items: task will be executed for each specifed items.
   903  if you specify string, it will be evaluated and result will be used as item.
   904  
   905  .. code-block:: yaml
   906  
   907      - eval: "[4, 5, 6]"
   908        register: "list2"
   909      - eval: "result += item"
   910        with_items:
   911         - 1
   912         - 2
   913         - 3
   914      - eval: "result += item"
   915        with_items: "list2"
   916  
   917  with_dict: loop over object. item will have key and value.
   918  
   919  .. code-block:: yaml
   920  
   921    - eval: "context[item.key] = item.value"
   922      with_dict:
   923        alice: 18
   924        bob: 21
   925  
   926  
   927  Supported tasks
   928  ^^^^^^^^^^^^^^^^^^^
   929  
   930  - debug  -- show debug message
   931  - eval -- eval javasciprt
   932  - sleep: miliseconds  sleep certin time
   933  - command: execute shell command
   934  
   935  .. code-block:: yaml
   936  
   937    - command:
   938       name: echo
   939       args:
   940        - test
   941  
   942  
   943  - fetch -- get data from db
   944  
   945    schema: schema id
   946    tenant_id: target tenant id
   947    id: id of the resource
   948  
   949  
   950  .. code-block:: yaml
   951  
   952    - fetch:
   953       schema: "network"
   954       id: "{{ response.network_id }}"
   955       tenant_id: ""
   956     register: network
   957  
   958  - list -- get data from db
   959  
   960    schema: schema id
   961    tenant_id: target tenant id
   962    id: id of the resource
   963  
   964  .. code-block:: yaml
   965  
   966    - list:
   967       schema: "network"
   968       tenant_id: ""
   969     register: network
   970  
   971  - contrail
   972  
   973    Create contrail resources
   974    In order to use this, you need to set correct api URL on etc/contrail/extensions/contrail_extension_config.js, and proper keystone configuraion
   975  
   976    id: remote resource uuid will be saved on this specificed property in
   977    main resource
   978    schema: contrail resource id
   979    allow_update: list of properties allowed to be updated
   980    properties: remote resource properties
   981  
   982  .. code-block:: yaml
   983  
   984    - contrail:
   985        id: "contrail_virtual_network"
   986        schema: "virtual-network"
   987        allow_update: []
   988        properties:
   989          parent_type: "project"
   990          fq_name:
   991            - default-domain
   992            - "{{ tenant_name }}"
   993            - "{{ response.id }}"
   994  
   995  
   996  - heat
   997  
   998    You can crud heat stack from donburi.
   999    This is a example
  1000  
  1001  .. code-block:: yaml
  1002  
  1003     - heat:
  1004         id: "heat_stack_id"
  1005         stack_name: "{{ response.name }}"
  1006         template:
  1007           heat_template_version: 2013-05-23
  1008           parameters: {}
  1009           resources:
  1010               server:
  1011                 type: OS::Nova::Server
  1012                 properties:
  1013                   image: "tinycore-in-network-nat"
  1014                   flavor: "m1.tiny"
  1015           outputs:
  1016             server_networks:
  1017               description: The networks of the deployed server
  1018               value: { get_attr: [server, networks] }
  1019  
  1020  - netconf
  1021  
  1022    You can use netconf to configure remote network devices.
  1023    Note that it is possible you get code injection attack if you
  1024    directly use user's input.
  1025  
  1026  .. code-block:: yaml
  1027  
  1028      tasks:
  1029        - block:
  1030            - netconf_open:
  1031                host: "{{.response.management_ip}}"
  1032                username: "admin"
  1033              register: session
  1034            - netconf_exec:
  1035                connection: session
  1036                command: "<get-config><source><running/></get-config>"
  1037              register: output
  1038            - debug: "{{.output.output.Data}}"
  1039          always:
  1040            - netconf_close: session
  1041  
  1042  - ssh
  1043  
  1044    You can use ssh to configure remote hosts.
  1045    Note that it is possible you get code injection attack if you
  1046    directly use user's input.
  1047  
  1048  .. code-block:: yaml
  1049  
  1050     - block:
  1051         - ssh_open:
  1052             host: "{{.response.management_ip}}:22"
  1053             username: "admin"
  1054           register: session
  1055           rescue:
  1056             - debug: "{{.error}}"
  1057         - ssh_exec:
  1058             connection: session
  1059             command: "show interfaces"
  1060           register: output
  1061           rescue:
  1062             - debug: "{{.error}}"
  1063         - debug: "{{.output.output}}"
  1064       always:
  1065         - ssh_close: session