diff --git a/doc/ref/modules/all/index.rst b/doc/ref/modules/all/index.rst index 164a27d667..19e6aa9dc5 100644 --- a/doc/ref/modules/all/index.rst +++ b/doc/ref/modules/all/index.rst @@ -407,6 +407,7 @@ execution modules supervisord suse_apache svn + swarm swift sysbench sysfs diff --git a/doc/ref/modules/all/salt.modules.swarm.rst b/doc/ref/modules/all/salt.modules.swarm.rst new file mode 100644 index 0000000000..59cd8ecdbd --- /dev/null +++ b/doc/ref/modules/all/salt.modules.swarm.rst @@ -0,0 +1,6 @@ +===================== +salt.modules.swarm +===================== + +.. automodule:: salt.modules.swarm + :memebers: diff --git a/doc/ref/returners/all/salt.returners.telegram_return.rst b/doc/ref/returners/all/salt.returners.telegram_return.rst new file mode 100644 index 0000000000..7a8d62c418 --- /dev/null +++ b/doc/ref/returners/all/salt.returners.telegram_return.rst @@ -0,0 +1,6 @@ +============================== +salt.returners.telegram_return +============================== + +.. automodule:: salt.returners.telegram_return + :members: \ No newline at end of file diff --git a/salt/modules/swarm.py b/salt/modules/swarm.py new file mode 100644 index 0000000000..2d4297531a --- /dev/null +++ b/salt/modules/swarm.py @@ -0,0 +1,414 @@ + +# -*- coding: utf-8 -*- +""" +Docker Swarm Module using Docker's Python SDK +============================================= + +:codeauthor: Tyler Jones + +.. versionadded:: Oxygen + +The Docker Swarm Module is used to manage and create Docker Swarms. + +Dependencies +------------ + +- Docker installed on the host +- Docker python sdk >= 2.5.1 + +Docker Python SDK +----------------- +pip install -U docker + +More information: https://docker-py.readthedocs.io/en/stable/ +""" +# Import python libraries +from __future__ import absolute_import +import json + +try: + import docker + HAS_DOCKER = True +except ImportError: + HAS_DOCKER = False + +__virtualname__ = 'swarm' + + +def __virtual__(): + ''' + Load this module if the docker python module is installed + ''' + if HAS_DOCKER: + return __virtualname__ + return False, 'The swarm module failed to load: Docker python module is not avaialble.' + + +def __init__(self): + __context__['client'] = docker.from_env() + __context__['server_name'] = __grains__['id'] + + +def swarm_tokens(): + ''' + Get the Docker Swarm Manager or Worker join tokens + + CLI Example: + + .. code-block:: bash + + salt '*' swarm.swarm_tokens + ''' + client = docker.APIClient(base_url='unix://var/run/docker.sock') + service = client.inspect_swarm() + return service['JoinTokens'] + + +def swarm_init(advertise_addr=str, + listen_addr=int, + force_new_cluster=bool): + ''' + Initalize Docker on Minion as a Swarm Manager + + advertise_addr + The ip of the manager + + listen_addr + Listen address used for inter-manager communication, + as well as determining the networking interface used + for the VXLAN Tunnel Endpoint (VTEP). + This can either be an address/port combination in + the form 192.168.1.1:4567, + or an interface followed by a port number, + like eth0:4567 + + force_new_cluster + Force a new cluster if True is passed + + CLI Example: + + .. code-block:: bash + + salt '*' swarm.swarm_init advertise_addr='192.168.50.10' listen_addr='0.0.0.0' force_new_cluster=False + ''' + try: + salt_return = {} + __context__['client'].swarm.init(advertise_addr, + listen_addr, + force_new_cluster) + output = 'Docker swarm has been Initalized on '+ __context__['server_name'] + ' and the worker/manager Join token is below' + salt_return.update({'Comment': output, + 'Tokens': swarm_tokens()}) + except TypeError: + salt_return = {} + salt_return.update({'Error': 'Please make sure your passing advertise_addr, listen_addr and force_new_cluster correctly.'}) + return salt_return + + +def joinswarm(remote_addr=int, + listen_addr=int, + token=str): + ''' + Join a Swarm Worker to the cluster + + remote_addr + The manager node you want to connect to for the swarm + + listen_addr + Listen address used for inter-manager communication if the node gets promoted to manager, + as well as determining the networking interface used for the VXLAN Tunnel Endpoint (VTEP) + + token + Either the manager join token or the worker join token. + You can get the worker or manager token via `salt '*' swarm.swarm_tokens` + + CLI Example: + + .. code-block:: bash + + salt '*' swarm.joinswarm remote_addr=192.168.50.10 listen_addr='0.0.0.0' token='SWMTKN-1-64tux2g0701r84ofq93zppcih0pe081akq45owe9ts61f30x4t-06trjugdu7x2z47j938s54il' + ''' + try: + salt_return = {} + __context__['client'].swarm.join(remote_addrs=[remote_addr], + listen_addr=listen_addr, + join_token=token) + output = __context__['server_name'] + ' has joined the Swarm' + salt_return.update({'Comment': output, 'Manager_Addr': remote_addr}) + except TypeError: + salt_return = {} + salt_return.update({'Error': 'Please make sure this minion is not part of a swarm and your passing remote_addr, listen_addr and token correctly.'}) + return salt_return + + +def leave_swarm(force=bool): + ''' + Force the minion to leave the swarm + + force + Will force the minion/worker/manager to leave the swarm + + CLI Example: + + .. code-block:: bash + + salt '*' swarm.leave_swarm force=False + ''' + salt_return = {} + __context__['client'].swarm.leave(force=force) + output = __context__['server_name'] + ' has left the swarm' + salt_return.update({'Comment': output}) + return salt_return + + +def service_create(image=str, + name=str, + command=str, + hostname=str, + replicas=int, + target_port=int, + published_port=int): + ''' + Create Docker Swarm Service Create + + image + The docker image + + name + Is the service name + + command + The docker command to run in the container at launch + + hostname + The hostname of the containers + + replicas + How many replicas you want running in the swarm + + target_port + The target port on the container + + published_port + port thats published on the host/os + + CLI Example: + + .. code-block:: bash + salt '*' swarm.service_create image=httpd name=Test_Service command=None hostname=salthttpd replicas=6 target_port=80 published_port=80 + ''' + try: + salt_return = {} + replica_mode = docker.types.ServiceMode('replicated', replicas=replicas) + ports = docker.types.EndpointSpec(ports={target_port: published_port}) + __context__['client'].services.create(name=name, + image=image, + command=command, + mode=replica_mode, + endpoint_spec=ports) + echoback = __context__['server_name'] + ' has a Docker Swarm Service running named ' + name + salt_return.update({'Info': echoback, + 'Minion': __context__['server_name'], + 'Name': name, + 'Image': image, + 'Command': command, + 'Hostname': hostname, + 'Replicas': replicas, + 'Target_Port': target_port, + 'Published_Port': published_port}) + except TypeError: + salt_return = {} + salt_return.update({'Error': 'Please make sure your passing arguments correctly [image, name, command, hostname, replicas, target_port and published_port]'}) + return salt_return + + +def swarm_service_info(service_name=str): + ''' + Swarm Service Information + + service_name + The name of the service that you want information on about the service + + CLI Example: + + .. code-block:: bash + salt '*' swarm.swarm_service_info service_name=Test_Service + ''' + try: + salt_return = {} + client = docker.APIClient(base_url='unix://var/run/docker.sock') + service = client.inspect_service(service=service_name) + getdata = json.dumps(service) + dump = json.loads(getdata) + version = dump['Version']['Index'] + name = dump['Spec']['Name'] + network_mode = dump['Spec']['EndpointSpec']['Mode'] + ports = dump['Spec']['EndpointSpec']['Ports'] + swarm_id = dump['ID'] + create_date = dump['CreatedAt'] + update_date = dump['UpdatedAt'] + labels = dump['Spec']['Labels'] + replicas = dump['Spec']['Mode']['Replicated']['Replicas'] + network = dump['Endpoint']['VirtualIPs'] + image = dump['Spec']['TaskTemplate']['ContainerSpec']['Image'] + for items in ports: + published_port = items['PublishedPort'] + target_port = items['TargetPort'] + published_mode = items['PublishMode'] + protocol = items['Protocol'] + salt_return.update({'Service Name': name, + 'Replicas': replicas, + 'Service ID': swarm_id, + 'Network': network, + 'Network Mode': network_mode, + 'Creation Date': create_date, + 'Update Date': update_date, + 'Published Port': published_port, + 'Target Port': target_port, + 'Published Mode': published_mode, + 'Protocol': protocol, + 'Docker Image': image, + 'Minion Id': __context__['server_name'], + 'Version': version}) + except TypeError: + salt_return = {} + salt_return.update({'Error': 'service_name arg is missing?'}) + return salt_return + + +def remove_service(service=str): + ''' + Remove Swarm Service + + service + The name of the service + + CLI Example: + + .. code-block:: bash + salt '*' swarm.remove_service service=Test_Service + ''' + try: + salt_return = {} + client = docker.APIClient(base_url='unix://var/run/docker.sock') + service = client.remove_service(service) + salt_return.update({'Service Deleted': service, + 'Minion ID': __context__['server_name']}) + except TypeError: + salt_return = {} + salt_return.update({'Error': 'service arg is missing?'}) + return salt_return + + +def node_ls(server=str): + ''' + Displays Information about Swarm Nodes with passing in the server + + server + The minion/server name + + CLI Example: + + .. code-block:: bash + salt '*' swarm.node_ls server=minion1 + ''' + try: + salt_return = {} + client = docker.APIClient(base_url='unix://var/run/docker.sock') + service = client.nodes(filters=({'name': server})) + getdata = json.dumps(service) + dump = json.loads(getdata) + for items in dump: + docker_version = items['Description']['Engine']['EngineVersion'] + platform = items['Description']['Platform'] + hostnames = items['Description']['Hostname'] + ids = items['ID'] + role = items['Spec']['Role'] + availability = items['Spec']['Availability'] + status = items['Status'] + Version = items['Version']['Index'] + salt_return.update({'Docker Version': docker_version, + 'Platform': platform, + 'Hostname': hostnames, + 'ID': ids, + 'Roles': role, + 'Availability': availability, + 'Status': status, + 'Version': Version}) + except TypeError: + salt_return = {} + salt_return.update({'Error': 'The server arg is missing or you not targeting a Manager node?'}) + return salt_return + + +def remove_node(node_id=str, force=bool): + ''' + Remove a node from a swarm and the target needs to be a swarm manager + + node_id + The node id from the return of swarm.node_ls + + force + Forcefully remove the node/minion from the service + + CLI Example: + + .. code-block:: bash + salt '*' swarm.remove_node node_id=z4gjbe9rwmqahc2a91snvolm5 force=false + ''' + client = docker.APIClient(base_url='unix://var/run/docker.sock') + try: + if force == 'True': + service = client.remove_node(node_id, force=True) + return service + else: + service = client.remove_node(node_id, force=False) + return service + except TypeError: + salt_return = {} + salt_return.update({'Error': 'Is the node_id and/or force=True/False missing?'}) + return salt_return + + +def update_node(availability=str, + node_name=str, + role=str, + node_id=str, + version=int): + ''' + Updates docker swarm nodes/needs to target a manager node/minion + + availability + Drain or Active + + node_name + minion/node + + role + role of manager or worker + + node_id + The Id and that can be obtained via swarm.node_ls + + version + Is obtained by swarm.node_ls + + CLI Example: + + .. code-block:: bash + salt '*' docker_util.update_node availability=drain node_name=minion2 role=worker node_id=3k9x7t8m4pel9c0nqr3iajnzp version=19 + ''' + client = docker.APIClient(base_url='unix://var/run/docker.sock') + try: + salt_return = {} + node_spec = {'Availability': availability, + 'Name': node_name, + 'Role': role} + client.update_node(node_id=node_id, + version=version, + node_spec=node_spec) + salt_return.update({'Node Information': node_spec}) + except TypeError: + salt_return = {} + salt_return.update({'Error': 'Make sure all args are passed [availability, node_name, role, node_id, version]'}) + return salt_return