import os
import time
import urllib.request
import json
import platform
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils import common_utils
from ansible.module_utils.common_info import DeployStatus
class NodeExporterInstaller:
SYSTEMCTL_CMD = "systemctl"
SERVICE_NAME = "node-exporter"
INSTALL_PATH = "/usr/local/bin/node_exporter"
def __init__(self):
self.module = AnsibleModule(
argument_spec=dict(
tags=dict(type="list", required=True),
resources_dir=dict(type="str", required=True),
ip_address=dict(type="str", required=True),
)
)
self.run_tags = self.module.params["tags"]
self.username = "node_exporter"
self.install_path = self.INSTALL_PATH
self.arch = platform.machine()
self.resources_dir = os.path.expanduser(self.module.params["resources_dir"])
self.ip_address = self.module.params["ip_address"]
self.config = {}
self.port = None
self.messages = []
def create_user(self):
user_exit, _, _ = self.module.run_command(["getent", "passwd", self.username])
group_exit, _, _ = self.module.run_command(["getent", "group", self.username])
if user_exit != 0 and group_exit != 0:
self.module.run_command(
["useradd", "-M", "-s", "/sbin/nologin", self.username], check_rc=True)
elif user_exit == 0 and group_exit == 0:
self.messages.append("The user {} already exists, skipping".format(self.username))
else:
self.module.fail_json(
msg="The user or group of {} is invalid, please delete it and try again".format(self.username),
rc=1, changed=False
)
def install_pkg(self):
tar_pkg = self._find_node_exporter_pkg("node_exporter*{}.tar.gz".format(self.arch))
self.module.run_command(["tar", "-xzf", tar_pkg, "-C", os.path.dirname(tar_pkg), "--strip-components=1"], check_rc=True)
pkg = self._find_node_exporter_pkg("node_exporter")
self.module.run_command(["chmod", "755", pkg], check_rc=True)
self.module.run_command(["cp", pkg, self.install_path], check_rc=True)
self.module.run_command(["chown", "root:root", self.install_path], check_rc=True)
def create_service(self):
cmd = [self.install_path,
"--web.listen-address=:{}".format(self.port)]
content = [
"[Unit]",
"Description=Prometheus Node-Exporter",
"Documentation=https://github.com/prometheus/node_exporter",
"After=network.target",
"[Service]",
"Type=simple",
"User={}".format(self.username),
"Group={}".format(self.username),
"ExecStart={}".format(" ".join(cmd)),
"Restart=always",
"RestartSec=2",
"[Install]",
"WantedBy=multi-user.target"
]
with open("/etc/systemd/system/node-exporter.service", 'w') as f:
f.write("\n".join(content))
def setup_service(self):
self.module.run_command([self.SYSTEMCTL_CMD, "daemon-reload"], check_rc=True)
self.module.run_command([self.SYSTEMCTL_CMD, "enable", self.SERVICE_NAME], check_rc=True)
self.module.run_command([self.SYSTEMCTL_CMD, "start", self.SERVICE_NAME], check_rc=True)
def wait_server_ready(self):
url = "http://{}:{}/metrics".format(self.ip_address, self.port)
interval = 5
timeout = self.config.get("connect_timeout", 60)
start_time = time.time()
while True:
try:
response = urllib.request.urlopen(url)
if response.getcode() == 200:
break
except Exception as e:
self.messages.append("Waiting for node_exporter service to be ready: {}".format(str(e)))
if time.time() - start_time > timeout:
self.messages.append("Install node_exporter success, but connect to the service failed")
self._module_failed()
time.sleep(interval)
def run(self):
try:
self._get_config()
self.create_user()
self.install_pkg()
self.create_service()
self.setup_service()
self.wait_server_ready()
except Exception as e:
self.messages.append(str(e))
self._module_failed()
self._module_success()
def _module_failed(self):
return self.module.fail_json(msg="\n".join(self.messages), rc=1, changed=False)
def _module_success(self):
return self.module.exit_json(msg="Install node_exporter success.", rc=0, changed=True)
def _find_node_exporter_pkg(self, pattern):
pkgs, msgs = common_utils.find_files(os.path.join(self.resources_dir, "NodeExporter*"), pattern)
self.messages.extend(msgs)
if not pkgs:
if "auto" in self.run_tags:
self.module.exit_json(std_out="can not find node_exporter package, node_exporter install skipped", rc=0,
result={DeployStatus.DEPLOY_STATUS: DeployStatus.SKIP},
changed=False)
else:
self.messages.append("can not find node_exporter package.")
self._module_failed()
return pkgs[0]
def _get_config(self):
config_path = self._find_node_exporter_pkg("npu_monitor_config.json")
with open(config_path, 'r') as f:
self.config = json.load(f).get("node_exporter", {})
self.port = self.config.get("port", 9100)
def main():
NodeExporterInstaller().run()
if __name__ == "__main__":
main()