How to Create Custom Ansible Wildfly Module – Part 2
Introduction
After creating Wildfly Server Group in the first part, it is time to move to the second part. This post will discuss about creating server instance under each server group. Because without a server instance, server group almost has no use. So.. let’s ready for Ansible Wildfly Module part 2!
Server
Server is an actual application instance. Unlike Server Group, which is a logical group, Server needs a host for its life cycle. A host can be physical or virtual, and a physical host can contains one or more server instances.
Wildfly Server management only works on domain mode as well. Moreover, if you want to know more about Wildfly and its running mode, please take a look on its documentation here.
Use Case
Since currently there is no Ansible built-in module for Wildfly Server management, therefore I want to create a custom module for this purpose.
Here are what the module should do:
- Create Server instance
- Start Server instance
- Stop Server instance
- Remove Server instance
Cheat Sheet
This how-to is running on Python 2.7, JDK 8, Wildfly 10.0.0 and using JBoss CLI management guide from this link.
Here are commands from JBoss CLI regarding Wildfly Server management. Of course, based on the use cases :).
1 2 3 4 5 6 7 8 9 10 11 |
# Create server instance /host=HOST_NAME/server-config=SERVER_NAME:add(group=SERVER_GROUP_NAME) # Start server instance /host=HOST_NAME/server-config=SERVER_NAME:start # Stop server instance /host=HOST_NAME/server-config=SERVER_NAME:stop # Remove server instance /host=HOST_NAME/server-config=SERVER_NAME:remove |
Step by Step
Alright, enough for cheat sheet. Too many cheating is not good, is it? Yeah, we are now ready to code Ansible Wildfly module part 2!
Step 1 – Create New Python File
Create file under library module and give it a name jcli_server.py
. Then add hash bang, import statement for Ansible module utils, subprocess module for invoking shell command and time module.
1 2 3 4 5 |
#!/usr/bin/python from ansible.module_utils.basic import * import subprocess import time |
Note: there is an additional import statement compare to previous post, which is time module. This module is needed in remove server function.
Step 2 – Create Function to Check Server Existence
In every use case implementation, we need to examine a Server with specific name already created or not. The reason behind is, in several cases, if a server already exists, then it will have an impact in the Ansible Wildfly module itself. Especially with the changed flag after module execution.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
def isServerAlreadyCreated(data): cmd = data['jboss_home'] + '/bin/jboss-cli.sh' cli = "/host=%s/server=%s:query" % (data['host'], data['server_config_name']) controller = "--controller=%s:%s" % (data['controller_host'],data['controller_port']) user = "-u=%s" % (data['user']) password = "-p=%s" % (data['password']) p = subprocess.Popen(["sh", cmd, "-c", cli, controller, user, password], stdout=subprocess.PIPE) result = p.communicate()[0] created = False remoteExists = False result = str(result) if "WFLYCTL0216" in result: created = False else: created = True if "WFLYPRT0053" in result: remoteExists = False else: remoteExists = True return remoteExists, created |
Note: this similar with the server group.
- We have six dictionary keys, which are
jboss_home
,server_group_name
,controller_host,
controller_port
,user
andpassword
. These keys will be defined in the main method. - Whenever the result contains
WFLYCTL0216
, it is assumed that the server with the given name already created. - Whenever the result contains
WFLYPRT0053
, it is assumed that the remote host cannot be reached.
Step 3 – Create Functions for Wildfly Server Maintenance
Unlike previous post, I will merge all functions related Wildfly Server in this step. We need four functions for this purpose. Those are server_present
, server_absent
, server_start
and server_stop
. Each function will first check isServerAlreadyPresent
in order to determine its next step.
server_present
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
def server_present(data): cmd = data['jboss_home'] + '/bin/jboss-cli.sh' controller = "--controller=%s:%s" % (data['controller_host'],data['controller_port']) user = "-u=%s" % (data['user']) password = "-p=%s" % (data['password']) exists, created = isServerAlreadyCreated(data) isError = False hasChanged = True meta = {} res = [] if not exists: mesg = "Could not connect http-remoting://%s:%s" % (data['controller_host'],data['controller_port']) meta = {"status": "Error", "response": mesg} isError = True hasChanged = False else: if not created: cli = "/host=%s/server-config=%s:add(group=%s,socket-binding-port-offset=%s,socket-binding-group=%s)" % (data['host'],data['server_config_name'],data['server_group_name'],data['server_socket_binding_port_offset'],data['server_group_socket']) p = subprocess.Popen(["sh", cmd, "-c", cli, controller, user, password], stdout=subprocess.PIPE) result,err = p.communicate() res.append(result) meta = {"status": "OK", "response": res} else: hasChanged = False resp = "Server %s already created" % (data['server_config_name']) meta = {"status" : "OK", "response" : resp} return isError, hasChanged, meta |
server_absent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
def server_absent(data): cmd = data['jboss_home'] + '/bin/jboss-cli.sh' controller = "--controller=%s:%s" % (data['controller_host'],data['controller_port']) user = "-u=%s" % (data['user']) password = "-p=%s" % (data['password']) exists, created = isServerAlreadyCreated(data) isError = False hasChanged = True meta = {} res = [] if not exists: mesg = "Could not connect http-remoting://%s:%s" % (data['controller_host'],data['controller_port']) meta = {"status": "Error", "response": mesg} isError = True hasChanged = False else: if not created: hasChanged = False resp = "Server %s does not exist" % (data['server_config_name']) meta = {"status" : "OK", "response" : resp} else: cli = "/host=%s/server-config=%s:stop" % (data['host'],data['server_config_name']) p = subprocess.Popen(["sh", cmd, "-c", cli, controller, user, password], stdout=subprocess.PIPE) result,err = p.communicate() res.append(result) result = str(result) while not "STOPPED" in result: time.sleep(0.5) cli = "/host=%s/server-config=%s:stop" % (data['host'],data['server_config_name']) p = subprocess.Popen(["sh", cmd, "-c", cli, controller, user, password], stdout=subprocess.PIPE) result = p.communicate()[0] result = str(result,'utf-8') cli = "/host=%s/server-config=%s:remove" % (data['host'],data['server_config_name']) p = subprocess.Popen(["sh", cmd, "-c", cli, controller, user, password], stdout=subprocess.PIPE) result,err = p.communicate() res.append(result) meta = {"status": "OK", "response": res} return isError, hasChanged, meta |
server_start
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
def server_start(data): cmd = data['jboss_home'] + '/bin/jboss-cli.sh' controller = "--controller=%s:%s" % (data['controller_host'],data['controller_port']) user = "-u=%s" % (data['user']) password = "-p=%s" % (data['password']) exists, created = isServerAlreadyCreated(data) isError = False hasChanged = True meta = {} if not exists: mesg = "Could not connect http-remoting://%s:%s" % (data['controller_host'],data['controller_port']) meta = {"status": "Error", "response": mesg} isError = True hasChanged = False else: if created: cli = "/host=%s/server-config=%s:start" % (data['host'],data['server_config_name']) p = subprocess.Popen(["sh", cmd, "-c", cli, controller, user, password], stdout=subprocess.PIPE) result,err = p.communicate() meta = {"status": "OK", "response": result} else: hasChanged = False resp = "Server %s does not exist" % (data['server_config_name']) meta = {"status" : "OK", "response" : resp} return isError, hasChanged, meta |
server_stop
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
def server_stop(data): cmd = data['jboss_home'] + '/bin/jboss-cli.sh' controller = "--controller=%s:%s" % (data['controller_host'],data['controller_port']) user = "-u=%s" % (data['user']) password = "-p=%s" % (data['password']) exists, created = isServerAlreadyCreated(data) isError = False hasChanged = True meta = {} if not exists: mesg = "Could not connect http-remoting://%s:%s" % (data['controller_host'],data['controller_port']) meta = {"status": "Error", "response": mesg} isError = True hasChanged = False else: if created: cli = "/host=%s/server-config=%s:stop" % (data['host'],data['server_config_name']) p = subprocess.Popen(["sh", cmd, "-c", cli, controller, user, password], stdout=subprocess.PIPE) result,err = p.communicate() meta = {"status": "OK", "response": result} else: hasChanged = False resp = "Server %s does not exist" % (data['server_config_name']) meta = {"status" : "OK", "response" : resp} return isError, hasChanged, meta |
Note:
- There are five additional fields keys, which are
host
server_config_name
server_group_name
server_socket_binding_port_offset
server_group_socket
- In
server_absent
function, we need to stop the server before removing it. Otherwise, it will return error because server is currently running. And we pause about half a second after executing stop command in order to give a chance for the server in a STOP state.
Step 4 – Define Main Function
We already collected 10 dictionary keys. Also, like always, add another one for defining Server state. So.. here is the main function looks like.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
def main(): fields = { "jboss_home" : {"required": True, "type": "str"}, "host": { "required": False, "default": "master", "type": "str" }, "server_group_name": { "required": True, "type": "str" }, "server_config_name": { "required": True, "type": "str" }, "server_socket_binding_port_offset": { "required": False, "default": 0, "type": "int" }, "server_group_socket": { "required": False, "default": "standard-sockets", "type": "str" }, "controller_host": { "required": False, "default": "localhost", "type": "str" }, "controller_port": { "required": False, "default": 9990, "type": "int" }, "user" : { "required": True, "type": "str" }, "password" : { "required": True, "type": "str" }, "state": { "default": "present", "choices": ['present', 'absent', 'start', 'stop'], "type": 'str' }, } choice_map = { "present": server_present, "absent": server_absent, "start": server_start, "stop": server_stop, } module = AnsibleModule(argument_spec=fields) is_error, has_changed, result = choice_map.get( module.params['state'])(module.params) if not is_error: module.exit_json(changed=has_changed, meta=result) else: module.fail_json(msg="Error creating server", meta=result) |
And set the main function.
1 2 |
if __name__ == '__main__': main() |
Step 5 – play.yml
Now it is time to update your play.yml
by adding jcli_server
module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
- hosts: localhost tasks: - name: Server group jcli_servergroup: jboss_home: /opt/wildfly/ server_group_name: group1 state: present user: wildfly password: password register: hasil - debug: var=hasil - name: Server jcli_server: jboss_home: /opt/wildfly/ host: master server_group_name: group1 server_config_name: server1 server_socket_binding_port_offset: 20 state: present user: wildfly password: password register: hasil - debug: var=hasil |
And it is time to run your playbook.
1 |
ansible-playbook play.yml |
And you will get similar result:
Conclusion
That’s all folks. Now we can configure Wildfly server via Ansible as well. In the next post I will bring how to set the JVM parameter for each server. Hopefully not more than a week after this post :).