Add Ansible Documentation to readthedocs
[appc/deployment.git] / docs / Ansible Adapter / Ansible Adapter.rst
1 ==================================
2 APPC Ansible Adapter Documentation
3 ==================================
4
5 This wiki provides documentation regarding the design, capabilities and
6 usage of the Ansible Extension for APP-C. Ansible_ is a an open-source
7 VNF management framework that allows provide an almost cli like set of
8 tools in a structured form. It is agentless in that the target VNF need
9 not have any additional software other than:
10
11 a) SSH server
12 b) Python >= 2.7 
13 c) Any necessary software that is specific to the VNF to run its functions. 
14
15 Any action (e.g configure, restart, health check etc) can be
16 executed on the VNF by constructing a **playbook (or set of playbooks)**
17 that is executed by an Ansible agent on the VNF via SSH.
18
19 The Ansible Extension for APP-C allows management of VNFs that support Ansible
20 through the following three additions :
21
22  - **An APP-C/Ansible Server interface:** Ansible libraries are written in python and hence cannot be executed natively from within the APP-C Karaf container. Instead, the design calls for an **Ansible Server** that can execute the Ansible playbooks and exposes a **REST** interface that is compliant with requirements of APP-C. These requirements are documented as the Server API Interface that any compliant Ansible Server must support. Exact implementation of the Ansible Server is left open and does not affect APP-C operations as long as the server follows the interface. For purposes of evaluation, a reference web server that implements this APP-C/Ansible Server interface has been developed and the code is available from the App-C ONAP repository under *appc-adapters/appc-ansible-adapter/appc-ansible-example-server*.
23
24  - **An APP-C Ansible adapter:** The ansible adapter is an OSGI bundle in the APP-C Karaf container that interacts with the Ansible Server . It is essentially a set of REST calls that performs two actions, submit request for a playbook to be executed, and if required get the results of the playbook after execution (if in synchronous mode).
25
26  - **Ansible Directed Graph (DG):** The Ansible Directed graph is generic DG that can be used to invoke any playbook via Ansible (and hence any APP-C action, since in Ansible, VNF actions map to playbooks) corresponding to an LCM action.
27
28 The architecture design for supporting Ansible is outlined in the diagram below :
29
30 |image0|
31
32 The workflow envisioned when Application Controller receives an event is
33 as follows :
34
35 1) Application Controller receives event from the Event Bus for an LCM action.
36 2) The appropriate LCM API invokes the Dispatcher which performs the relevant lookups for A&AI and Workflow (DG information).
37 3) The dispatcher calls the DG relevant to the LCM action (for the VNF).
38 4) The DG conducts any processing of data (e.g retrieving additional information, filling templates etc) , prepares the necessary DG context variables outlined in Table 1 and then invokes the Ansible DG.
39 5) Ansible DG leverages the Ansible Adapter to interact with the Ansible Server.
40 6) Ansible Server invokes the appropriate playbook which in turn interacts with the VNF and then returns the playbook results.
41 7) Ansible Server returns results.
42 8) Ansible DG provides these results back to calling DG.
43
44 A ladder diagram of the work flow is pasted below :
45
46 |image1|
47
48 Details of each of these three (DG, Adapter and Ansible Server) are listed below :
49
50 1.  **Ansible Directed Graph (DG):** The Ansible Directed graph is the most common way an App-C developer is expected to leverage Ansible functionality. The Ansible DG is a general purpose graph that can be used to invoke and retrieve results from any playbook on an ONAP-compliant Ansible Server. The Ansible Graph,when called, expects a certain set of inputs to be provided as input and when upon completion provides results from the execution of the Ansible playbook. The Ansible
51 DG can be invoked using the following (current) naming:
52
53 +------------+----------------------+
54 | Field      | Value                |
55 +============+======================+ 
56 | module     | APPC                 |
57 +------------+----------------------+
58 | rpc        | ansible-adapter-1.0  | 
59 +------------+----------------------+
60 | version    | 2.0.1                | 
61 +------------+----------------------+
62
63 The inputs that the Ansible DG expects in DG context memory are listed below:
64
65 Table 1: Input Parameters to the Ansible Directed Graph
66
67 +----------------+-----------------------------------------------------------------+-----------------------+--------------------------------------------------------------------------+
68 | Variable Name  | Description                                                     | Type                  | Comments                                                                 |
69 +================+=================================================================+=======================+==========================================================================+
70 | User           | Username to logon to Ansible Server                             |  Mandatory            | Should be provided by APPC.                                              |
71 +----------------+-----------------------------------------------------------------+-----------------------+--------------------------------------------------------------------------+
72 | Password       | Password to logon to Ansible Server                             |  Mandatory            | Should be provided by APPC.                                              |
73 +----------------+-----------------------------------------------------------------+-----------------------+--------------------------------------------------------------------------+
74 | AgentUrl       | The complete URL of the Ansible Server to post the request for  |  Mandatory            | Should be provided by APPC.                                              |
75 |                | execution and retrieve results (if in synchronous mode)         |                       |                                                                          |
76 +----------------+-----------------------------------------------------------------+-----------------------+--------------------------------------------------------------------------+
77 | PlaybookName   | Name/identifier of playbook to run                              |  Mandatory            | To be provided in the template.                                          |
78 +----------------+-----------------------------------------------------------------+-----------------------+--------------------------------------------------------------------------+
79 | Action         | The type of VNF action being requested                          |  Optional             | Provided either by APPC or Template.                                     |
80 +----------------+-----------------------------------------------------------------+-----------------------+--------------------------------------------------------------------------+
81 | EnvParameters  | A JSON dictionary (stringified) listing the parameters to be    |  Optional             | Structure of the EnvParameters dictionary to be supplied in template.    |
82 |                | passed to the Ansible playbook                                  |                       | Values to be filled in by App-C based on values from payload in run-time |
83 +----------------+-----------------------------------------------------------------+-----------------------+--------------------------------------------------------------------------+
84 | FileParameters | A JSON dictionary (stringified) listing file names and files to |  Optional             | Structure of the FileParameters dictionary to be supplied in template.   |
85 |                | be created for Ansible playbook                                 |                       | Values to be filled in by App-C based on values from payload in run-time |
86 +----------------+-----------------------------------------------------------------+-----------------------+--------------------------------------------------------------------------+
87 | Timeout        | Time Ansible Server should wait before terminating playbook     |  Optional             | To be provided in the template.                                          |
88 +----------------+-----------------------------------------------------------------+-----------------------+--------------------------------------------------------------------------+
89 | NodeList       | List of FQDNs/IP Addresses of VNF that the Ansible playbook     |  Optional             | To be provided to App-C during Runtime.                                  |
90 |                | should be executed on.                                          |  (if not supplied,    |                                                                          |
91 |                |                                                                 |  will run on server)  |                                                                          |
92 +----------------+-----------------------------------------------------------------+-----------------------+--------------------------------------------------------------------------+
93
94   The 'template' referred in the above table must be a JSON file as documented in the VNF vendor requirements and must contain the key-value pairs listed above (that are expected to be in the template). An LCM API Directed graph should fill in necessary parameters in the template, and then put the key-value pairs from the template as listed above in DG context memory before calling the Ansible DG.
95
96 Upon completion the Ansible DG sets the following variables in DG context memory
97
98 Table 2: Output Variables set by Ansible DG Variable
99
100 +-----------------------+--------------------------------------------------------------------------+
101 | Type                  | Comments                                                                 |
102 +=======================+==========================================================================+
103 | output.status.code    | Result of the request: 400 if SUCCESS , 200 if FAILURE.                  |
104 |                       |                                                                          |
105 |                       | The ansible playbook may have multiple sub-tasks, playbooks etc and may  |
106 |                       | run on multiple VMs of a host. The request is considered to fail if even |
107 |                       | one of the tasks is incomplete.                                          |
108 +-----------------------+--------------------------------------------------------------------------+
109 | output.status.message | If playbook finished, set to FINISH, if playbook terminated, set to      |
110 |                       | TERMINATED. If abnormal error, reported in message                       |
111 +-----------------------+--------------------------------------------------------------------------+
112 | output.status.results | A JSON dictionary with results corresponding to output provided by the   |
113 |                       | Ansible playbook request. This is optional (may not be present if        |
114 |                       | playbook was terminated). The results, if present, will be in the form   |
115 |                       | of a dictionary that follows the format presented in the Ansible Server  |
116 |                       | API Documentation. The document also contains examples of output.        |
117 +-----------------------+--------------------------------------------------------------------------+
118
119   *Note : The Ansible Server supports a Callback Url functionality, but it is currently not invoked by App-C Ansible Adapter or Directed Graph. If added, it is easy to change the Adapter and Ansible DG to support this.*
120
121 2.  **APP-C Ansible Adapter:** The App-C Ansible Adapter is an OSGI bundle which essentially makes REST calls to the Ansible Server. It exposes three methods that can be invoked by the Service Logic Interpreter (SLI).
122
123   a. *void reqExec(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException*: A method to invoke the test.
124
125   b. *void reqExecResult(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException*:  A method to request results of a test.
126
127   c. *void reqExecLog(Map<String, String> params, SvcLogicContext ctx) throws SvcLogicException* : A method to retreive the logs from a request (not used in the Ansible DG currently).
128
129     Currently, the Ansible DG uses only the first two (reqExec and reqExecResult) since only these two are needed to request execution of a playbook and retrieval of results. The reqExecLog is for diagnostic purposes.
130
131     In order to communicate with the Ansible Server, it is currently assumed that:
132
133     a. Credentials comprise of a username and password.
134
135     b. Communication is over https
136
137     The Ansible Adapter has three configurable parameters related to SSL certificate of the Ansible Server, which can be set from the properties file:
138
139     a. org.openecomp.appc.adapter.ansible.clientType. If set to "TRUST\_ALL", will accept all SSL certificates from any Ansible Server. If set to "TRUST\_CERT", will accept SSL from only those Ansible Servers whose certificate is in the trustStore keystore file. These two options can be used for development environment. Default option is to trust only well known server certificates (use in Production).
140
141     b. org.openecomp.appc.adapter.ansible.trustStore used to point to the keystore file
142
143     c. org.openecomp.appc.adapter.ansible.trustStorePasswd used to set password for keystore file
144
145 3.  **Reference Ansible Server Implementation of APPC / Ansible Interface (for testing purposes only)**
146
147     a. Overview
148
149     |image2|
150
151     b. Inventory file
152
153       The Prototype Ansible Server requires that all credentials and IP Addresses for the VNF being tested either already be present in the Server’s Database or be loaded before any playbooks are invoked. Supported credentials are user-name/password and public-key authentication. 
154
155       All VNF credentials stored in a unique file (or in a SQL database depending on the ansible server runtime configuration):
156
157       [host]
158       localhost ansible\_connection=local
159
160       [hostgroup1] hostname11 ansible\_connection=ssh
161       ansible\_ssh\_user=loginid11 ansible\_ssh\_pass=passwd11 hostname12
162       ansible\_connection=ssh ansible\_ssh\_user=loginid12
163       ansible\_ssh\_private\_key\_file=kefile12 … [hostgroup2] hostname21
164       ansible\_connection=ssh ansible\_ssh\_user=loginid21
165       ansible\_ssh\_private\_key\_file=keyfile21 …. [hostgroup3] …
166
167     c. Playbooks
168
169       Playbooks can either be provided as stand alone text files or gzipped tar file (playbooks with roles sub-directories) either stored in a local file or in an SQL database.
170
171       Naming convention: anything\_LCM@M.mn.{yml,tar.gz} where version number M is a digit and mn are subversion number digits.
172
173       Playbooks should be written such that they can run from the command line: "ansible-playbook -i inventoryfile –extra-vars optionalvariables playbookname" That means the playbook should not contain any VM credentials information, they are expected to be provided through the inventory file passed at run time.
174
175         a.  Stand-alone playbooks
176
177         |image3|
178
179         b.  Playbooks in gzipped tarfiles
180
181         |image4|
182
183     d. Installation
184
185       a. Python
186
187         sudo apt-get install python2.7
188         sudo apt-get install python-pip
189         pip install PyMySQL
190         pip install requests
191
192       b. Ansible
193
194         sudo apt-get install software-properties-common
195         sudo apt-add-repository ppa:ansible/ansible
196         sudo apt-get update 
197         sudo apt-get install ansible 
198
199       c. SQL database
200
201         a. Installing MySQL
202
203           sudo apt-get install mysql-server
204
205           Set root passwd during installation (i.e. password\_4\_mysql\_user\_id)
206
207           sudo service mysql restart
208
209         b. Setting up mysql
210
211           mysql -u [username]-p
212
213           mysql -uroot -p
214
215           Create user (i.e. id=mysql\_user\_id psswd=password\_4\_mysql\_user\_id)
216
217           CREATE USER 'appc'@'%' IDENTIFIED BY 'password\_4\_mysql\_user\_id';
218
219           GRANT ALL PRIVILEGES ON *.* TO 'mysql\_user\_id'@'%';
220
221           SET PASSWORD FOR 'mysql\_user\_id'@'%'=PASSWORD('password\_4\_mysql\_user\_id');
222
223         c. Creating schema
224
225           CREATE SCHEMA ansible;
226
227           SHOW DATABASES;
228
229           USE ansible;
230
231           CREATE TABLE playbook (name VARCHAR(45) NOT NULL, value BLOB, type VARCHAR(60), version VARCHAR(60), PRIMARY KEY (name));
232
233           SHOW TABLES;
234
235           CREATE TABLE inventory (hostname VARCHAR(45) NOT NULL, hostgroup VARCHAR(45), credentials VARCHAR(500), PRIMARY KEY (hostname));
236
237           SHOW COLUMNS FROM playbook;
238
239           SHOW COLUMNS FROM inventory;
240
241           GRANT ALL PRIVILEGES ON *.* TO 'mysql\_user\_id'@'%' IDENTIFIED BY 'password\_4\_mysql\_user\_id' WITH GRANT OPTION;
242
243           GRANT ALL PRIVILEGES ON *.* TO 'ansible'@'%' IDENTIFIED BY 'ansible\_agent' WITH GRANT OPTION;
244
245           FLUSH PRIVILEGES;
246
247
248
249         d. Loading playbooks and inventory data in SQL database
250
251           Place inventory file and playbooks to be loaded in one directory, set LoadAnsibleMySql variables:
252
253           SQL credentials:
254
255           host="localhost" # your host, usually localhost
256           user="mysql\_user\_id" # your username
257           passwd="password\_4\_mysql\_user\_id" # your password
258           db="ansible" # name of the database
259
260
261           Path of playbook location:
262
263           playbook\_path = "something/something/"
264
265
266           Full name of inventory file:
267
268           inventory = "something/something/Ansible\_inventory"
269
270           These variables are located right after main:
271
272           |image5|
273
274           Run loader: python LoadAnsibleMySql.py
275
276     e. Execution
277
278       Ansible server is executed through RestServer.py. Its configuration file consists of the following:
279
280       # Host definition
281       ip: 0.0.0.0
282       port: 8000
283       # Security (controls use of TLS encrypton and RestServer authentication)
284       tls: no
285       auth: no
286       # TLS certificates (must be built on application host)
287       priv: provide\_privated\_key.pem
288       pub: provide\_public\_key.pem 
289       # RestServer authentication
290       id: provide\_RestServer\_id
291       psswd: provide\_password\_4\_RestServer\_id
292       # Mysql
293       host: localhost
294       user: mysql\_user\_id
295       passwd: password\_4\_mysql\_user\_id
296       db: ansible 
297       #Playbooks
298       from\_files: yes
299       ansible\_path: /home/ubuntu/RestServerOpenSource
300       ansible\_inv: Ansible\_inventory
301       ansible\_temp: PlaybooksTemp
302       timeout\_seconds: 60
303       # Blocking on GetResults
304       getresults\_block: yes
305
306 Execution and testing steps:
307
308  1.  **Start RestServer**: *python RestServer.py*
309
310    Note: RSA key fingerprint needs to be loaded manually in server for each VM defined in inventory file that requires ssh authentication. This can be done by testing ssh credentials to each target VM and accepting RSA key fingerprint:
311
312    .. code:: bash
313
314      ssh -i key \|VMaddress\|
315      RSA key fingerprint is \|something.\|
316      Are you sure you want to continue connecting (yes/no)? yes
317
318
319  2.  **Try curl commands** (case no secured REST: HTTP & no authentication)
320
321    Request to execute playbook:
322
323    .. code:: bash
324
325      curl -H "Content-type: application/json" -X POST -d '{"Id": "10", "PlaybookName": "ansible\_sleep", "NodeList": ["host"], "Timeout": "60", "EnvParameters": {"Sleep": "10"}}'http://0.0.0.0:8000/Dispatch
326
327    Response:
328
329    .. code:: bash
330
331      {"ExpectedDuration": "60sec", "StatusMessage": "PENDING", "StatusCode": 100}
332
333    Get results (blocked until test finished):
334
335    .. code:: bash
336
337      curl -H "Content-type: application/json" -X GET "http://0.0.0.0:8000/Dispatch/?Id=10&Type=GetResult"
338
339    Response:
340
341    .. code:: bash
342
343      {"Results": {"localhost": {"GroupName": "host", "StatusMessage": "SUCCESS", "StatusCode": 200}}, "PlaybookName":"ansible\_sleep", "Version": "0.00", "Duration": "11.261794", "StatusMessage": "FINISHED", "StatusCode": 200}
344
345    Delete playbook execution information
346
347    .. code:: bash
348
349      curl -H "Content-type: application/json" -X DELETE http://0.0.0.0:8000/Dispatch/?Id=10
350
351    Response:
352
353    .. code:: bash
354
355      {"StatusMessage": "PLAYBOOK EXECUTION RECORDS DELETED", "StatusCode": 200}
356
357 Playbook execution done through system call
358
359    .. code:: bash
360
361      ansible-playbook --v -extra-vars ‘playbookvars’ -i inventoryfile playbook.yml
362
363    - Inventory file created at run time, playbook loaded from mysql, both placed in the temporary directory destroyed at end of test (Playbook archive is unpacked in the temporary directory)
364
365 All tested playbooks written such that the ansible ‘play recap’ log indicates whether or not the playbook tasks succeeded (multiple tasks in a standalone playbook or playbooks with roles directory structure)
366
367 Sample ansible ‘play recap’:
368
369 |image6|
370
371
372
373 .. _Ansible: https://www.ansible.com/
374
375 .. |image0| image:: images/image0.png
376 .. |image1| image:: images/image1.png
377 .. |image2| image:: images/image2.png
378 .. |image3| image:: images/image3.png
379 .. |image4| image:: images/image4.png
380 .. |image5| image:: images/image5.png
381 .. |image6| image:: images/image6.png