From a4e8aa23dcec3679b5a3cbd5866ec94d2339b9ff Mon Sep 17 00:00:00 2001 From: Jan David Mol <mol@astron.nl> Date: Sun, 19 Sep 2021 18:54:37 +0200 Subject: [PATCH] L2SS-379: Added device to monitor, stop, start our docker containers. --- devices/devices/docker.py | 163 ++++++++++++++++++ docker-compose/device-docker.yml | 44 +++++ .../lofar-device-base/lofar-requirements.txt | 1 + 3 files changed, 208 insertions(+) create mode 100644 devices/devices/docker.py create mode 100644 docker-compose/device-docker.yml diff --git a/devices/devices/docker.py b/devices/devices/docker.py new file mode 100644 index 000000000..f4ed88eb0 --- /dev/null +++ b/devices/devices/docker.py @@ -0,0 +1,163 @@ +# -*- coding: utf-8 -*- +# +# This file is part of the Docker project +# +# +# +# Distributed under the terms of the APACHE license. +# See LICENSE.txt for more info. + +""" Docker Device Server for LOFAR2.0 + +""" + +# TODO(Corne): Remove sys.path.append hack once packaging is in place! +import os, sys +currentdir = os.path.dirname(os.path.realpath(__file__)) +parentdir = os.path.dirname(currentdir) +sys.path.append(parentdir) + +# PyTango imports +from tango import DebugIt +from tango.server import run, command +from tango.server import device_property, attribute +from tango import AttrWriteType +# Additional import + +from device_decorators import * + +from clients.docker_client import DockerClient +from clients.attribute_wrapper import attribute_wrapper +from common.lofar_logging import device_logging_to_python, log_exceptions +from common.lofar_git import get_version + +__all__ = ["Docker", "main"] + +@device_logging_to_python() +class Docker(hardware_device): + """ + + **Properties:** + + - Device Property + OPC_Server_Name + - Type:'DevString' + OPC_Server_Port + - Type:'DevULong' + OPC_Time_Out + - Type:'DevDouble' + """ + + # ----------------- + # Device Properties + # ----------------- + + Docker_Base_URL = device_property( + dtype='DevString', + mandatory=False, + default_value="unix:///var/run/docker.sock" + ) + + # ---------- + # Attributes + # ---------- + version_R = attribute(dtype=str, access=AttrWriteType.READ, fget=lambda self: get_version()) + archiver_maria_db_R = attribute_wrapper(comms_annotation=["archiver-maria-db"], datatype=numpy.bool_) + archiver_maria_db_RW = attribute_wrapper(comms_annotation=["archiver-maria-db"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + databaseds_R = attribute_wrapper(comms_annotation=["databaseds"], datatype=numpy.bool_) + databaseds_RW = attribute_wrapper(comms_annotation=["databaseds"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + device_pcc_R = attribute_wrapper(comms_annotation=["device_pcc"], datatype=numpy.bool_) + device_pcc_RW = attribute_wrapper(comms_annotation=["device_pcc"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + device_sdp_R = attribute_wrapper(comms_annotation=["device_sdp"], datatype=numpy.bool_) + device_sdp_RW = attribute_wrapper(comms_annotation=["device_sdp"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + device_sst_R = attribute_wrapper(comms_annotation=["device_sst"], datatype=numpy.bool_) + device_sst_RW = attribute_wrapper(comms_annotation=["device_sst"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + device_xst_R = attribute_wrapper(comms_annotation=["device_xst"], datatype=numpy.bool_) + device_xst_RW = attribute_wrapper(comms_annotation=["device_xst"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + device_unb2_R = attribute_wrapper(comms_annotation=["device_unb2"], datatype=numpy.bool_) + device_unb2_RW = attribute_wrapper(comms_annotation=["device_unb2"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + docker_R = attribute_wrapper(comms_annotation=["docker"], datatype=numpy.bool_) + # docker_RW is not available, as we cannot start our own container` + dsconfig_R = attribute_wrapper(comms_annotation=["dsconfig"], datatype=numpy.bool_) + dsconfig_RW = attribute_wrapper(comms_annotation=["dsconfig"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + elk_R = attribute_wrapper(comms_annotation=["elk"], datatype=numpy.bool_) + elk_RW = attribute_wrapper(comms_annotation=["elk"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + grafana_R = attribute_wrapper(comms_annotation=["grafana"], datatype=numpy.bool_) + grafana_RW = attribute_wrapper(comms_annotation=["grafana"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + hdbpp_cm_R = attribute_wrapper(comms_annotation=["hdbpp-cm"], datatype=numpy.bool_) + hdbpp_cm_RW = attribute_wrapper(comms_annotation=["hdbpp-cm"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + hdbpp_es_R = attribute_wrapper(comms_annotation=["hdbpp-es"], datatype=numpy.bool_) + hdbpp_es_RW = attribute_wrapper(comms_annotation=["hdbpp-es"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + itango_R = attribute_wrapper(comms_annotation=["itango"], datatype=numpy.bool_) + itango_RW = attribute_wrapper(comms_annotation=["itango"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + jupyter_R = attribute_wrapper(comms_annotation=["jupyter"], datatype=numpy.bool_) + jupyter_RW = attribute_wrapper(comms_annotation=["jupyter"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + prometheus_R = attribute_wrapper(comms_annotation=["prometheus"], datatype=numpy.bool_) + prometheus_RW = attribute_wrapper(comms_annotation=["prometheus"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + tangodb_R = attribute_wrapper(comms_annotation=["tangodb"], datatype=numpy.bool_) + # tangodb_RW is not available, as we cannot start tango from this device if it is down + tango_prometheus_exporter_R = attribute_wrapper(comms_annotation=["tango-prometheus-exporter"], datatype=numpy.bool_) + tango_prometheus_exporter_RW = attribute_wrapper(comms_annotation=["tango-prometheus-exporter"], datatype=numpy.bool_, access=AttrWriteType.READ_WRITE) + + @log_exceptions() + def delete_device(self): + """Hook to delete resources allocated in init_device. + + This method allows for any memory or other resources allocated in the + init_device method to be released. This method is called by the device + destructor and by the device Init command (a Tango built-in). + """ + self.debug_stream("Shutting down...") + + self.Off() + self.debug_stream("Shut down. Good bye.") + + # -------- + # overloaded functions + # -------- + @log_exceptions() + def configure_for_off(self): + """ user code here. is called when the state is set to OFF """ + # Stop keep-alive + try: + self.opcua_connection.stop() + except Exception as e: + self.warn_stream("Exception while stopping OPC ua connection in configure_for_off function: {}. Exception ignored".format(e)) + + @log_exceptions() + def configure_for_initialise(self): + """ user code here. is called when the state is set to INIT """ + + # set up the Docker client + self.docker_client = DockerClient(self.Docker_Base_URL, self.Fault, self) + + # map an access helper class + for i in self.attr_list(): + try: + i.set_comm_client(self.docker_client) + except Exception as e: + # use the pass function instead of setting read/write fails + i.set_pass_func() + self.warn_stream("error while setting the attribute {} read/write function. {}".format(i, e)) + + self.docker_client.start() + + # -------- + # Commands + # -------- + + +# ---------- +# Run server +# ---------- +def main(args=None, **kwargs): + """Main function of the Docker module.""" + + from common.lofar_logging import configure_logger + configure_logger() + + return run((Docker,), args=args, **kwargs) + + +if __name__ == '__main__': + main() diff --git a/docker-compose/device-docker.yml b/docker-compose/device-docker.yml new file mode 100644 index 000000000..78f43f18e --- /dev/null +++ b/docker-compose/device-docker.yml @@ -0,0 +1,44 @@ +# +# Docker compose file that launches an interactive iTango session. +# +# Connect to the interactive session with 'docker attach itango'. +# Disconnect with the Docker deattach sequence: <CTRL>+<P> <CTRL>+<Q> +# +# Defines: +# - itango: iTango interactive session +# +# Requires: +# - lofar-device-base.yml +# +version: '2' + +services: + device-docker: + image: device-docker + # build explicitly, as docker-compose does not understand a local image + # being shared among services. + build: + context: lofar-device-base + args: + SOURCE_IMAGE: ${DOCKER_REGISTRY_HOST}/${DOCKER_REGISTRY_USER}-tango-itango:${TANGO_ITANGO_VERSION} + container_name: ${CONTAINER_NAME_PREFIX}device-docker + networks: + - control + ports: + - "5705:5705" # unique port for this DS + volumes: + - ${TANGO_LOFAR_CONTAINER_MOUNT} + - /var/run/docker.sock:/var/run/docker.sock:rw + privileged: true + environment: + - TANGO_HOST=${TANGO_HOST} + entrypoint: + - /usr/local/bin/wait-for-it.sh + - ${TANGO_HOST} + - --timeout=30 + - --strict + - -- + # configure CORBA to _listen_ on 0:port, but tell others we're _reachable_ through ${HOSTNAME}:port, since CORBA + # can't know about our Docker port forwarding + - python3 -u ${TANGO_LOFAR_CONTAINER_DIR}/devices/devices/docker.py LTS -v -ORBendPoint giop:tcp:0:5705 -ORBendPointPublish giop:tcp:${HOSTNAME}:5705 + restart: on-failure diff --git a/docker-compose/lofar-device-base/lofar-requirements.txt b/docker-compose/lofar-device-base/lofar-requirements.txt index 57ab2a14f..2214412a4 100644 --- a/docker-compose/lofar-device-base/lofar-requirements.txt +++ b/docker-compose/lofar-device-base/lofar-requirements.txt @@ -4,3 +4,4 @@ python-logstash-async gitpython PyMySQL[rsa] sqlalchemy +docker -- GitLab