docker_swarm update to use shared Swarm library (#52886)
* docker_swarm: Update code to use AnsibleDockerSwarmClient instead of AnsibleDockerClient * docker_swarm: Update check_if_swarm_node_is_down() with repetitive attempts to check node status to reflect original method implementation * docker_swarm: Add information that `state: inspect` will be removed in future release * docker_swarm: Fix sanity error * docker_swarm: Check_mode conditional for failing during the swarm init * docker_swarm: Small cleanup of a code * docker_swarm: Moving the warning message before dispatching * Commit to solve problems with Shippablepull/4420/head
parent
c6ae23062b
commit
72bdcdfff2
|
@ -3,6 +3,7 @@
|
||||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from docker.errors import APIError
|
from docker.errors import APIError
|
||||||
|
@ -108,23 +109,29 @@ class AnsibleDockerSwarmClient(AnsibleDockerClient):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def check_if_swarm_node_is_down(self, node_id=None):
|
def check_if_swarm_node_is_down(self, node_id=None, repeat_check=1):
|
||||||
"""
|
"""
|
||||||
Checks if node status on Swarm manager is 'down'. If node_id is provided it query manager about
|
Checks if node status on Swarm manager is 'down'. If node_id is provided it query manager about
|
||||||
node specified in parameter, otherwise it query manager itself. If run on Swarm Worker node or
|
node specified in parameter, otherwise it query manager itself. If run on Swarm Worker node or
|
||||||
host that is not part of Swarm it will fail the playbook
|
host that is not part of Swarm it will fail the playbook
|
||||||
|
|
||||||
|
:param repeat_check: number of check attempts with 5 seconds delay between them, by default check only once
|
||||||
:param node_id: node ID or name, if None then method will try to get node_id of host module run on
|
:param node_id: node ID or name, if None then method will try to get node_id of host module run on
|
||||||
:return:
|
:return:
|
||||||
True if node is part of swarm but its state is down, False otherwise
|
True if node is part of swarm but its state is down, False otherwise
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if repeat_check < 1:
|
||||||
|
repeat_check = 1
|
||||||
|
|
||||||
if node_id is None:
|
if node_id is None:
|
||||||
node_id = self.get_swarm_node_id()
|
node_id = self.get_swarm_node_id()
|
||||||
|
|
||||||
node_info = self.get_node_inspect(node_id=node_id)
|
for retry in range(0, repeat_check):
|
||||||
if node_info['Status']['State'] == 'down':
|
node_info = self.get_node_inspect(node_id=node_id)
|
||||||
return True
|
if node_info['Status']['State'] == 'down':
|
||||||
|
return True
|
||||||
|
sleep(5)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_node_inspect(self, node_id=None, skip_missing=False):
|
def get_node_inspect(self, node_id=None, skip_missing=False):
|
||||||
|
|
|
@ -159,6 +159,7 @@ requirements:
|
||||||
- Docker API >= 1.25
|
- Docker API >= 1.25
|
||||||
author:
|
author:
|
||||||
- Thierry Bouvet (@tbouvet)
|
- Thierry Bouvet (@tbouvet)
|
||||||
|
- Piotr Wojciechowski (@WojciechowskiPiotr)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
|
@ -229,7 +230,7 @@ actions:
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from time import sleep
|
|
||||||
try:
|
try:
|
||||||
from docker.errors import APIError
|
from docker.errors import APIError
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -237,10 +238,12 @@ except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from ansible.module_utils.docker.common import (
|
from ansible.module_utils.docker.common import (
|
||||||
AnsibleDockerClient,
|
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
DifferenceTracker,
|
DifferenceTracker,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from ansible.module_utils.docker.swarm import AnsibleDockerSwarmClient
|
||||||
|
|
||||||
from ansible.module_utils._text import to_native
|
from ansible.module_utils._text import to_native
|
||||||
|
|
||||||
|
|
||||||
|
@ -253,6 +256,7 @@ class TaskParameters(DockerBaseClass):
|
||||||
self.force_new_cluster = None
|
self.force_new_cluster = None
|
||||||
self.remote_addrs = None
|
self.remote_addrs = None
|
||||||
self.join_token = None
|
self.join_token = None
|
||||||
|
self.force = None
|
||||||
|
|
||||||
# Spec
|
# Spec
|
||||||
self.snapshot_interval = None
|
self.snapshot_interval = None
|
||||||
|
@ -389,6 +393,11 @@ class SwarmManager(DockerBaseClass):
|
||||||
"inspect": self.inspect_swarm
|
"inspect": self.inspect_swarm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.state == 'inspect':
|
||||||
|
self.client.module.deprecate(
|
||||||
|
"The 'inspect' state is deprecated, please use 'docker_swarm_facts' to inspect swarm cluster",
|
||||||
|
version='2.12')
|
||||||
|
|
||||||
choice_map.get(self.state)()
|
choice_map.get(self.state)()
|
||||||
|
|
||||||
if self.client.module._diff or self.parameters.debug:
|
if self.client.module._diff or self.parameters.debug:
|
||||||
|
@ -396,15 +405,6 @@ class SwarmManager(DockerBaseClass):
|
||||||
diff['before'], diff['after'] = self.differences.get_before_after()
|
diff['before'], diff['after'] = self.differences.get_before_after()
|
||||||
self.results['diff'] = diff
|
self.results['diff'] = diff
|
||||||
|
|
||||||
def __isSwarmManager(self):
|
|
||||||
try:
|
|
||||||
data = self.client.inspect_swarm()
|
|
||||||
json_str = json.dumps(data, ensure_ascii=False)
|
|
||||||
self.swarm_info = json.loads(json_str)
|
|
||||||
return True
|
|
||||||
except APIError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def inspect_swarm(self):
|
def inspect_swarm(self):
|
||||||
try:
|
try:
|
||||||
data = self.client.inspect_swarm()
|
data = self.client.inspect_swarm()
|
||||||
|
@ -416,7 +416,7 @@ class SwarmManager(DockerBaseClass):
|
||||||
return
|
return
|
||||||
|
|
||||||
def init_swarm(self):
|
def init_swarm(self):
|
||||||
if self.__isSwarmManager():
|
if self.client.check_if_swarm_manager():
|
||||||
self.__update_swarm()
|
self.__update_swarm()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -428,7 +428,10 @@ class SwarmManager(DockerBaseClass):
|
||||||
except APIError as exc:
|
except APIError as exc:
|
||||||
self.client.fail("Can not create a new Swarm Cluster: %s" % to_native(exc))
|
self.client.fail("Can not create a new Swarm Cluster: %s" % to_native(exc))
|
||||||
|
|
||||||
self.__isSwarmManager()
|
if not self.client.check_if_swarm_manager():
|
||||||
|
if not self.check_mode:
|
||||||
|
self.client.fail("Swarm not created or other error!")
|
||||||
|
self.inspect_swarm()
|
||||||
self.results['actions'].append("New Swarm cluster created: %s" % (self.swarm_info.get('ID')))
|
self.results['actions'].append("New Swarm cluster created: %s" % (self.swarm_info.get('ID')))
|
||||||
self.differences.add('state', parameter='absent', active='present')
|
self.differences.add('state', parameter='absent', active='present')
|
||||||
self.results['changed'] = True
|
self.results['changed'] = True
|
||||||
|
@ -460,26 +463,15 @@ class SwarmManager(DockerBaseClass):
|
||||||
self.results['actions'].append("Swarm cluster updated")
|
self.results['actions'].append("Swarm cluster updated")
|
||||||
self.results['changed'] = True
|
self.results['changed'] = True
|
||||||
|
|
||||||
def __isSwarmNode(self):
|
|
||||||
info = self.client.info()
|
|
||||||
if info:
|
|
||||||
json_str = json.dumps(info, ensure_ascii=False)
|
|
||||||
self.swarm_info = json.loads(json_str)
|
|
||||||
if self.swarm_info['Swarm']['NodeID']:
|
|
||||||
return True
|
|
||||||
if self.swarm_info['Swarm']['LocalNodeState'] in ('active', 'pending', 'locked'):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def join(self):
|
def join(self):
|
||||||
if self.__isSwarmNode():
|
if self.client.check_if_swarm_node():
|
||||||
self.results['actions'].append("This node is already part of a swarm.")
|
self.results['actions'].append("This node is already part of a swarm.")
|
||||||
return
|
return
|
||||||
if not self.check_mode:
|
if not self.check_mode:
|
||||||
try:
|
try:
|
||||||
self.client.join_swarm(
|
self.client.join_swarm(
|
||||||
remote_addrs=self.parameters.remote_addrs, join_token=self.parameters.join_token, listen_addr=self.parameters.listen_addr,
|
remote_addrs=self.parameters.remote_addrs, join_token=self.parameters.join_token,
|
||||||
advertise_addr=self.parameters.advertise_addr)
|
listen_addr=self.parameters.listen_addr, advertise_addr=self.parameters.advertise_addr)
|
||||||
except APIError as exc:
|
except APIError as exc:
|
||||||
self.client.fail("Can not join the Swarm Cluster: %s" % to_native(exc))
|
self.client.fail("Can not join the Swarm Cluster: %s" % to_native(exc))
|
||||||
self.results['actions'].append("New node is added to swarm cluster")
|
self.results['actions'].append("New node is added to swarm cluster")
|
||||||
|
@ -487,7 +479,7 @@ class SwarmManager(DockerBaseClass):
|
||||||
self.results['changed'] = True
|
self.results['changed'] = True
|
||||||
|
|
||||||
def leave(self):
|
def leave(self):
|
||||||
if not(self.__isSwarmNode()):
|
if not(self.client.check_if_swarm_node()):
|
||||||
self.results['actions'].append("This node is not part of a swarm.")
|
self.results['actions'].append("This node is not part of a swarm.")
|
||||||
return
|
return
|
||||||
if not self.check_mode:
|
if not self.check_mode:
|
||||||
|
@ -499,33 +491,16 @@ class SwarmManager(DockerBaseClass):
|
||||||
self.differences.add('joined', parameter='absent', active='present')
|
self.differences.add('joined', parameter='absent', active='present')
|
||||||
self.results['changed'] = True
|
self.results['changed'] = True
|
||||||
|
|
||||||
def __get_node_info(self):
|
|
||||||
try:
|
|
||||||
node_info = self.client.inspect_node(node_id=self.parameters.node_id)
|
|
||||||
except APIError as exc:
|
|
||||||
raise exc
|
|
||||||
json_str = json.dumps(node_info, ensure_ascii=False)
|
|
||||||
node_info = json.loads(json_str)
|
|
||||||
return node_info
|
|
||||||
|
|
||||||
def __check_node_is_down(self):
|
|
||||||
for _x in range(0, 5):
|
|
||||||
node_info = self.__get_node_info()
|
|
||||||
if node_info['Status']['State'] == 'down':
|
|
||||||
return True
|
|
||||||
sleep(5)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def remove(self):
|
def remove(self):
|
||||||
if not(self.__isSwarmManager()):
|
if not(self.client.check_if_swarm_manager()):
|
||||||
self.client.fail("This node is not a manager.")
|
self.client.fail("This node is not a manager.")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
status_down = self.__check_node_is_down()
|
status_down = self.client.check_if_swarm_node_is_down(repeat_check=5)
|
||||||
except APIError:
|
except APIError:
|
||||||
return
|
return
|
||||||
|
|
||||||
if not(status_down):
|
if not status_down:
|
||||||
self.client.fail("Can not remove the node. The status node is ready and not down.")
|
self.client.fail("Can not remove the node. The status node is ready and not down.")
|
||||||
|
|
||||||
if not self.check_mode:
|
if not self.check_mode:
|
||||||
|
@ -577,7 +552,7 @@ def main():
|
||||||
ca_force_rotate=dict(docker_api_version='1.30'),
|
ca_force_rotate=dict(docker_api_version='1.30'),
|
||||||
)
|
)
|
||||||
|
|
||||||
client = AnsibleDockerClient(
|
client = AnsibleDockerSwarmClient(
|
||||||
argument_spec=argument_spec,
|
argument_spec=argument_spec,
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
required_if=required_if,
|
required_if=required_if,
|
||||||
|
|
Loading…
Reference in New Issue